mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 09:13:14 +00:00
feat: Add component for write technical notes tool call (#395)
* feat: Add component for write technical notes tool call * cr
This commit is contained in:
parent
caf222235f
commit
d769983483
4 changed files with 145 additions and 12 deletions
|
|
@ -12,6 +12,7 @@ import { formatCustomRulesPrompt } from "../../../utils/custom-rules.js";
|
|||
import { getPlannerNotes } from "../utils/get-notes.js";
|
||||
import { ToolMessage } from "@langchain/core/messages";
|
||||
import { DO_NOT_RENDER_ID_PREFIX } from "@open-swe/shared/constants";
|
||||
import { createWriteTechnicalNotesToolFields } from "@open-swe/shared/open-swe/tools";
|
||||
|
||||
const PLANNER_NOTES_PROMPT = `You've also taken technical notes throughout the context gathering process. Ensure you include/incorporate these notes, or the highest quality parts of these notes in your conclusion notes.
|
||||
|
||||
|
|
@ -93,17 +94,7 @@ const formatPrompt = (state: PlannerGraphState): string => {
|
|||
);
|
||||
};
|
||||
|
||||
const condenseContextToolSchema = z.object({
|
||||
notes: z
|
||||
.string()
|
||||
.describe("The notes you've generated based on the conversation history."),
|
||||
});
|
||||
const condenseContextTool = {
|
||||
name: "write_technical_notes",
|
||||
description:
|
||||
"Write technical notes based on the conversation history provided. Ensure these notes are concise, but still containing enough information to be useful to you when you go to execute the plan.",
|
||||
schema: condenseContextToolSchema,
|
||||
};
|
||||
const condenseContextTool = createWriteTechnicalNotesToolFields();
|
||||
|
||||
export async function notetaker(
|
||||
state: PlannerGraphState,
|
||||
|
|
@ -144,7 +135,7 @@ ${state.messages.map(getMessageString).join("\n")}`;
|
|||
return {
|
||||
messages: [response, toolResponse],
|
||||
contextGatheringNotes: (
|
||||
toolCall.args as z.infer<typeof condenseContextToolSchema>
|
||||
toolCall.args as z.infer<typeof condenseContextTool.schema>
|
||||
).notes,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
94
apps/web/src/components/gen-ui/write-technical-notes.tsx
Normal file
94
apps/web/src/components/gen-ui/write-technical-notes.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import {
|
||||
CheckCircle,
|
||||
Loader2,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
FileText,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type WriteTechnicalNotesProps = {
|
||||
status: "generating" | "done";
|
||||
notes?: string;
|
||||
reasoningText?: string;
|
||||
};
|
||||
|
||||
export function WriteTechnicalNotes({
|
||||
status,
|
||||
notes,
|
||||
reasoningText,
|
||||
}: WriteTechnicalNotesProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const getStatusIcon = () => {
|
||||
switch (status) {
|
||||
case "generating":
|
||||
return (
|
||||
<Loader2 className="text-muted-foreground size-3.5 animate-spin" />
|
||||
);
|
||||
case "done":
|
||||
return <CheckCircle className="size-3.5 text-green-500" />;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = () => {
|
||||
switch (status) {
|
||||
case "generating":
|
||||
return "Writing technical notes...";
|
||||
case "done":
|
||||
return "Technical notes written";
|
||||
}
|
||||
};
|
||||
|
||||
const shouldShowToggle = () => {
|
||||
return status === "done" && notes;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border-border overflow-hidden rounded-md border">
|
||||
<div className="border-border flex items-center border-b bg-gray-50 p-2 dark:bg-gray-800">
|
||||
<FileText className="text-muted-foreground mr-2 size-3.5" />
|
||||
<span className="text-foreground/80 flex-1 text-xs font-normal">
|
||||
Technical Notes
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground text-xs font-normal">
|
||||
{getStatusText()}
|
||||
</span>
|
||||
{getStatusIcon()}
|
||||
{shouldShowToggle() && (
|
||||
<button
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
{expanded ? (
|
||||
<ChevronUp className="size-3.5" />
|
||||
) : (
|
||||
<ChevronDown className="size-3.5" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{expanded && notes && status === "done" && (
|
||||
<div className="bg-muted overflow-x-auto p-2 dark:bg-gray-900">
|
||||
<pre className="text-foreground/90 text-xs font-normal whitespace-pre-wrap">
|
||||
{notes}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{reasoningText && status === "done" && (
|
||||
<div className="border-t border-blue-300 bg-blue-100/50 p-2 dark:border-blue-800 dark:bg-blue-900/50">
|
||||
<p className="text-xs font-normal text-blue-700 dark:text-blue-300">
|
||||
{reasoningText}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import { ActionStep, ActionItemProps } from "@/components/gen-ui/action-step";
|
|||
import { TaskSummary } from "@/components/gen-ui/task-summary";
|
||||
import { PullRequestOpened } from "@/components/gen-ui/pull-request-opened";
|
||||
import { DiagnoseErrorAction } from "@/components/v2/diagnose-error-action";
|
||||
import { WriteTechnicalNotes } from "@/components/gen-ui/write-technical-notes";
|
||||
import { ToolCall } from "@langchain/core/messages/tool";
|
||||
import {
|
||||
createApplyPatchToolFields,
|
||||
|
|
@ -34,6 +35,7 @@ import {
|
|||
createDiagnoseErrorToolFields,
|
||||
createGetURLContentToolFields,
|
||||
createFindInstancesOfToolFields,
|
||||
createWriteTechnicalNotesToolFields,
|
||||
} from "@open-swe/shared/open-swe/tools";
|
||||
import { z } from "zod";
|
||||
import { isAIMessageSDK, isToolMessageSDK } from "@/lib/langchain-messages";
|
||||
|
|
@ -67,6 +69,11 @@ type GetURLContentToolArgs = z.infer<typeof getURLContentTool.schema>;
|
|||
const findInstancesOfTool = createFindInstancesOfToolFields(dummyRepo);
|
||||
type FindInstancesOfToolArgs = z.infer<typeof findInstancesOfTool.schema>;
|
||||
|
||||
const writeTechnicalNotesTool = createWriteTechnicalNotesToolFields();
|
||||
type WriteTechnicalNotesToolArgs = z.infer<
|
||||
typeof writeTechnicalNotesTool.schema
|
||||
>;
|
||||
|
||||
function CustomComponent({
|
||||
message,
|
||||
thread,
|
||||
|
|
@ -305,6 +312,10 @@ export function AssistantMessage({
|
|||
? aiToolCalls.find((tc) => tc.name === diagnoseErrorTool.name)
|
||||
: undefined;
|
||||
|
||||
const writeTechnicalNotesToolCall = message
|
||||
? aiToolCalls.find((tc) => tc.name === writeTechnicalNotesTool.name)
|
||||
: undefined;
|
||||
|
||||
// We can be sure that if the task status tool call is present, it will be the
|
||||
// only tool call/result we need to render for this message.
|
||||
if (taskStatusToolCall) {
|
||||
|
|
@ -346,6 +357,26 @@ export function AssistantMessage({
|
|||
);
|
||||
}
|
||||
|
||||
if (writeTechnicalNotesToolCall) {
|
||||
const correspondingToolResult = toolResults.find(
|
||||
(tr) => tr && tr.tool_call_id === writeTechnicalNotesToolCall.id,
|
||||
);
|
||||
|
||||
const args =
|
||||
writeTechnicalNotesToolCall.args as WriteTechnicalNotesToolArgs;
|
||||
const reasoningText = getContentString(content);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<WriteTechnicalNotes
|
||||
status={correspondingToolResult ? "done" : "generating"}
|
||||
notes={args.notes}
|
||||
reasoningText={reasoningText}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (openPrToolCall) {
|
||||
let branch: string | undefined;
|
||||
let targetBranch: string | undefined = "main";
|
||||
|
|
|
|||
|
|
@ -342,3 +342,20 @@ export function createGetURLContentToolFields() {
|
|||
schema: getURLContentSchema,
|
||||
};
|
||||
}
|
||||
|
||||
export function createWriteTechnicalNotesToolFields() {
|
||||
const writeTechnicalNotesSchema = z.object({
|
||||
notes: z
|
||||
.string()
|
||||
.describe(
|
||||
"The notes you've generated based on the conversation history.",
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
name: "write_technical_notes",
|
||||
description:
|
||||
"Write technical notes based on the conversation history provided. Ensure these notes are concise, but still containing enough information to be useful to you when you go to execute the plan.",
|
||||
schema: writeTechnicalNotesSchema,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue