mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 10:23:14 +00:00
fix: Manager routing, and UI issues (#440)
* fix: Manager routing, and UI issues * cr * cr * cr
This commit is contained in:
parent
928b68fd36
commit
92014bdd11
11 changed files with 68 additions and 54 deletions
|
|
@ -23,3 +23,7 @@ export const LANGGRAPH_USER_PERMISSIONS = [
|
|||
"deployments:search",
|
||||
"store:access",
|
||||
];
|
||||
|
||||
export enum RequestSource {
|
||||
GITHUB_ISSUE_WEBHOOK = "github_issue_webhook",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import { PLANNER_GRAPH_ID } from "@open-swe/shared/constants";
|
|||
import { createLogger, LogLevel } from "../../../../utils/logger.js";
|
||||
import { PlannerGraphState } from "@open-swe/shared/open-swe/planner/types";
|
||||
import { createClassificationPromptAndToolSchema } from "./utils.js";
|
||||
import { RequestSource } from "../../../../constants.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "ClassifyMessage");
|
||||
|
||||
|
|
@ -86,6 +87,9 @@ export async function classifyMessage(
|
|||
messages: state.messages,
|
||||
taskPlan,
|
||||
proposedPlan: issuePlans?.proposedPlan ?? undefined,
|
||||
requestSource: userMessage.additional_kwargs?.requestSource as
|
||||
| RequestSource
|
||||
| undefined,
|
||||
});
|
||||
const respondAndRouteTool = {
|
||||
name: "respond_and_route",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { RequestSource } from "../../../../constants.js";
|
||||
|
||||
export const UPDATE_PROGRAMMER_ROUTING_OPTION = `- update_programmer: You should call this route if the user's message should be added to the programmer's currently running session. This should be called if you determine the user is trying to provide extra context to the programmer's current session.\n`;
|
||||
|
||||
export const START_PLANNER_ROUTING_OPTION = `- start_planner: You should call this route if the user's message is a complete request you can send to the planner, which it can use to generate a plan. This route may be called when the planner has not started yet.\n`;
|
||||
|
|
@ -41,6 +43,9 @@ Ensure your response is clear, and concise.
|
|||
|
||||
Although you're only supposed to classify & respond to the latest message, this does not mean you should look at it in isolation. You should consider the conversation history as a whole, and the current status of your two AI assistants (programmer and planner) to determine how to respond & route the user's new message.
|
||||
|
||||
If the source is from a '${RequestSource.GITHUB_ISSUE_WEBHOOK}', you should ALWAYS classify it as a full request which should be routed to the planner.
|
||||
The instances where the source will be '${RequestSource.GITHUB_ISSUE_WEBHOOK}' are when the user labels a GitHub issue as a task to be completed by the AI coding agent system.
|
||||
|
||||
# Context
|
||||
Although it's not shown here, you do have access to the full repository contents the user is referencing. Because of this, you should always assume you'll have access to any/all files or folders the user is referencing.
|
||||
|
||||
|
|
@ -48,6 +53,9 @@ Although it's not shown here, you do have access to the full repository contents
|
|||
The planner's current status is: {PLANNER_STATUS}
|
||||
The programmer's current status is: {PROGRAMMER_STATUS}
|
||||
|
||||
# Source
|
||||
The source of the request is: {REQUEST_SOURCE}
|
||||
|
||||
{TASK_PLAN_PROMPT}
|
||||
{CONVERSATION_HISTORY_PROMPT}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import {
|
|||
START_PLANNER_FOR_FOLLOWUP_ROUTING_OPTION,
|
||||
} from "./prompts.js";
|
||||
import { createClassificationSchema } from "./schemas.js";
|
||||
import { RequestSource } from "../../../../constants.js";
|
||||
|
||||
const THREAD_STATUS_READABLE_STRING_MAP = {
|
||||
not_started: "not started",
|
||||
|
|
@ -72,6 +73,7 @@ export function createClassificationPromptAndToolSchema(inputs: {
|
|||
messages: BaseMessage[];
|
||||
taskPlan: TaskPlan;
|
||||
proposedPlan?: string[];
|
||||
requestSource?: RequestSource;
|
||||
}): {
|
||||
prompt: string;
|
||||
schema: z.ZodTypeAny;
|
||||
|
|
@ -166,6 +168,10 @@ export function createClassificationPromptAndToolSchema(inputs: {
|
|||
.replaceAll(
|
||||
"{CONVERSATION_HISTORY_PROMPT}",
|
||||
formattedConversationHistoryPrompt ?? "",
|
||||
)
|
||||
.replaceAll(
|
||||
"{REQUEST_SOURCE}",
|
||||
inputs.requestSource ?? "no source provided",
|
||||
);
|
||||
|
||||
const schema = createClassificationSchema(
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
getOpenSWELabel,
|
||||
} from "../../utils/github/label.js";
|
||||
import { ManagerGraphUpdate } from "@open-swe/shared/open-swe/manager/types";
|
||||
import { RequestSource } from "../../constants.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "GitHubIssueWebhook");
|
||||
|
||||
|
|
@ -149,6 +150,7 @@ webhooks.on("issues.labeled", async ({ payload }) => {
|
|||
additional_kwargs: {
|
||||
isOriginalIssue: true,
|
||||
githubIssueId: issueData.issueNumber,
|
||||
requestSource: RequestSource.GITHUB_ISSUE_WEBHOOK,
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -148,4 +148,25 @@
|
|||
.shadow-inner-left {
|
||||
box-shadow: inset 9px 0 6px -1px rgb(0 0 0 / 0.02);
|
||||
}
|
||||
|
||||
.scrollbar-pretty-auto {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.scrollbar-pretty-auto::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.scrollbar-pretty-auto::-webkit-scrollbar-thumb {
|
||||
border-radius: 9999px;
|
||||
background-color: rgb(209 213 219);
|
||||
}
|
||||
|
||||
.scrollbar-pretty-auto::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.dark .scrollbar-pretty-auto::-webkit-scrollbar-thumb {
|
||||
background-color: rgb(75 85 99);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,47 +145,16 @@ function MatchCaseIcon({ matchCase }: { matchCase: boolean }) {
|
|||
);
|
||||
}
|
||||
|
||||
function MatchWholeWordIcon({ matchWholeWord }: { matchWholeWord: boolean }) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
className={cn(
|
||||
"rounded-sm border border-gray-300 px-1 py-[2px] text-xs dark:border-gray-600",
|
||||
matchWholeWord
|
||||
? "border-blue-500 bg-blue-500/80 text-white"
|
||||
: "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200",
|
||||
)}
|
||||
>
|
||||
<span className="relative inline-block px-0.5">
|
||||
<p className="relative z-10 m-0 text-center font-mono">ab</p>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute bottom-0 left-0 h-1/5 w-[1px]",
|
||||
matchWholeWord ? "bg-white" : "bg-gray-600 dark:bg-gray-300",
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute right-0 bottom-0 left-0 h-[1px] w-full",
|
||||
matchWholeWord ? "bg-white" : "bg-gray-600 dark:bg-gray-300",
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute right-0 bottom-0 h-1/5 w-[1px]",
|
||||
matchWholeWord ? "bg-white" : "bg-gray-600 dark:bg-gray-300",
|
||||
)}
|
||||
></div>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
Match whole word {matchWholeWord ? "on" : "off"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
const coerceStringToArray = (str: string | string[]) => {
|
||||
if (Array.isArray(str)) {
|
||||
return str;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
return [str];
|
||||
}
|
||||
};
|
||||
|
||||
function ActionItem(props: ActionItemProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
|
@ -379,7 +348,10 @@ function ActionItem(props: ActionItemProps) {
|
|||
<span>Max results: {castProps.max_results}</span>
|
||||
)}
|
||||
{castProps.file_types && castProps.file_types.length > 0 && (
|
||||
<span>File types: {castProps.file_types.join(", ")}</span>
|
||||
<span>
|
||||
File types:{" "}
|
||||
{coerceStringToArray(castProps.file_types).join(", ")}
|
||||
</span>
|
||||
)}
|
||||
{castProps.follow_symlinks && <span>Follow symlinks</span>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ const defaultComponents: any = {
|
|||
pre: ({ className, ...props }: { className?: string }) => (
|
||||
<pre
|
||||
className={cn(
|
||||
"max-w-4xl overflow-x-auto rounded-lg bg-black text-white",
|
||||
"w-full overflow-x-auto rounded-lg bg-black text-white",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -217,7 +217,7 @@ const defaultComponents: any = {
|
|||
const code = String(children).replace(/\n$/, "");
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full overflow-hidden rounded-lg">
|
||||
<CodeHeader
|
||||
language={language}
|
||||
code={code}
|
||||
|
|
@ -228,7 +228,7 @@ const defaultComponents: any = {
|
|||
>
|
||||
{code}
|
||||
</SyntaxHighlighter>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ export const SyntaxHighlighter: FC<SyntaxHighlighterProps> = ({
|
|||
customStyle={{
|
||||
margin: 0,
|
||||
width: "100%",
|
||||
maxWidth: "100%",
|
||||
background: "transparent",
|
||||
padding: "1.5rem 1rem",
|
||||
overflow: "auto",
|
||||
}}
|
||||
className={className}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,10 @@ import { AnimatePresence, motion } from "framer-motion";
|
|||
import { Bot, Copy, CopyCheck, Send, User, Loader2 } from "lucide-react";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { Button } from "../ui/button";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import { ManagerGraphState } from "@open-swe/shared/open-swe/manager/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { isAIMessageSDK } from "@/lib/langchain-messages";
|
||||
import { BasicMarkdownText } from "../thread/markdown-text";
|
||||
|
||||
function MessageCopyButton({ content }: { content: string }) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
|
|
@ -98,7 +97,7 @@ export function ManagerChat({
|
|||
initial={true}
|
||||
>
|
||||
<StickyToBottomContent
|
||||
className="h-full overflow-y-auto"
|
||||
className="scrollbar-pretty-auto h-full"
|
||||
contentClassName="space-y-4 p-4"
|
||||
content={
|
||||
<>
|
||||
|
|
@ -121,7 +120,7 @@ export function ManagerChat({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex-1 space-y-1">
|
||||
<div className="relative min-w-0 flex-1 space-y-1 overflow-x-hidden">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="text-muted-foreground text-xs font-medium">
|
||||
{message.type === "human" ? "You" : "Agent"}
|
||||
|
|
@ -130,7 +129,7 @@ export function ManagerChat({
|
|||
<MessageCopyButton content={messageContentString} />
|
||||
</div>
|
||||
</div>
|
||||
<BasicMarkdownText className="text-foreground text-sm">
|
||||
<BasicMarkdownText className="text-foreground overflow-x-hidden text-sm">
|
||||
{messageContentString}
|
||||
</BasicMarkdownText>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ module.exports = {
|
|||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
components: {
|
||||
".scrollbar-pretty":
|
||||
"overflow-y-scroll [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-gray-300 [&::-webkit-scrollbar-track]:bg-transparent",
|
||||
},
|
||||
colors: {
|
||||
background: "hsl(var(--background))",
|
||||
foreground: "hsl(var(--foreground))",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue