Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion app/[docs_id]/chatForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ import { useChatHistory, type Message } from "../hooks/useChathistory";
import useSWR from "swr";
import { getQuestionExample } from "../actions/questionExample";
import { getLanguageName } from "../pagesList";
import { ReplCommand, ReplOutput } from "../terminal/repl";

interface ChatFormProps {
documentContent: string;
sectionId: string;
replOutputs: ReplCommand[];
fileContents: Array<{
name: string;
content: string;
}>;
execResults: Record<string, ReplOutput[]>;
}

export function ChatForm({ documentContent, sectionId }: ChatFormProps) {
export function ChatForm({ documentContent, sectionId, replOutputs, fileContents, execResults }: ChatFormProps) {
const [messages, updateChatHistory] = useChatHistory(sectionId);
const [inputValue, setInputValue] = useState("");
const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -56,6 +63,9 @@ export function ChatForm({ documentContent, sectionId }: ChatFormProps) {
const result = await askAI({
userQuestion,
documentContent: documentContent,
replOutputs,
fileContents,
execResults,
});

if (result.error) {
Expand Down
11 changes: 7 additions & 4 deletions app/[docs_id]/section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,12 @@ interface SectionProps {

// 1つのセクションのタイトルと内容を表示する。内容はMarkdownとしてレンダリングする
export function Section({ section, sectionId }: SectionProps) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [replOutputs, setReplOutputs] = useState<ReplCommand[]>([]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [execResults, setExecResults] = useState<Record<string, ReplOutput[]>>(
{}
);
const [filenames, setFilenames] = useState<string[]>([]);
const { files } = useFile();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const fileContents: { name: string; content: string }[] = filenames.map(
(name) => ({ name, content: files[name] || "" })
);
Expand Down Expand Up @@ -76,7 +73,13 @@ export function Section({ section, sectionId }: SectionProps) {
<div>
<Heading level={section.level}>{section.title}</Heading>
<StyledMarkdown content={section.content} />
<ChatForm documentContent={section.content} sectionId={sectionId} />
<ChatForm
documentContent={section.content}
sectionId={sectionId}
replOutputs={replOutputs}
fileContents={fileContents}
execResults={execResults}
/>
</div>
</SectionCodeContext.Provider>
);
Expand Down
59 changes: 57 additions & 2 deletions app/actions/chatActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ interface FormState {
const ChatSchema = z.object({
userQuestion: z.string().min(1, { message: "メッセージを入力してください。" }),
documentContent: z.string().min(1, { message: "コンテキストとなるドキュメントがありません。"}),
replOutputs: z.array(z.object({
command: z.string(),
output: z.array(z.object({
type: z.enum(["stdout", "stderr", "error", "return", "trace", "system"]),
message: z.string(),
})),
})).optional(),
fileContents: z.array(z.object({
name: z.string(),
content: z.string(),
})).optional(),
execResults: z.record(z.string(), z.array(z.object({
type: z.enum(["stdout", "stderr", "error", "return", "trace", "system"]),
message: z.string(),
}))).optional(),
});

type ChatParams = z.input<typeof ChatSchema>;
Expand All @@ -25,22 +40,62 @@ export async function askAI(params: ChatParams): Promise<FormState> {
};
}

const { userQuestion, documentContent } = parseResult.data;
const { userQuestion, documentContent, replOutputs, fileContents, execResults } = parseResult.data;

try {
// ターミナルログの文字列を構築
let terminalLogsSection = "";
if (replOutputs && replOutputs.length > 0) {
terminalLogsSection = "\n# ターミナルのログ(ユーザーが入力したコマンドとその実行結果)\n";
for (const replCmd of replOutputs) {
terminalLogsSection += `\n## コマンド: ${replCmd.command}\n`;
terminalLogsSection += "```\n";
for (const output of replCmd.output) {
terminalLogsSection += `${output.message}\n`;
}
terminalLogsSection += "```\n";
}
}

// ファイルエディターの内容を構築
let fileContentsSection = "";
if (fileContents && fileContents.length > 0) {
fileContentsSection = "\n# ファイルエディターの内容\n";
for (const file of fileContents) {
fileContentsSection += `\n## ファイル: ${file.name}\n`;
fileContentsSection += "```\n";
fileContentsSection += file.content;
fileContentsSection += "\n```\n";
}
}

// ファイル実行結果を構築
let execResultsSection = "";
if (execResults && Object.keys(execResults).length > 0) {
execResultsSection = "\n# ファイルの実行結果\n";
for (const [filename, outputs] of Object.entries(execResults)) {
execResultsSection += `\n## ファイル: ${filename}\n`;
execResultsSection += "```\n";
for (const output of outputs) {
execResultsSection += `${output.message}\n`;
}
execResultsSection += "```\n";
}
}

const prompt = `
以下のPythonチュートリアルのドキュメントの内容を正確に理解し、ユーザーからの質問に対して、初心者にも分かりやすく、丁寧な解説を提供してください。

# ドキュメント
${documentContent}

${terminalLogsSection}${fileContentsSection}${execResultsSection}
# ユーザーからの質問
${userQuestion}

# 指示
- 回答はMarkdown形式で記述し、コードブロックを適切に使用してください。
- ドキュメントの内容に基づいて回答してください。
- ユーザーが入力したターミナルのコマンドやファイルの内容、実行結果を参考にして回答してください。
- ユーザーへの回答のみを出力してください。
- 必要であれば、具体的なコード例を提示してください。
-
Expand Down