mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 05:43:14 +00:00
fix: A handful of small fixes (#408)
This commit is contained in:
parent
78f38503e0
commit
a1d9b88d69
25 changed files with 556 additions and 92 deletions
|
|
@ -89,7 +89,7 @@ Run these commands from the repository root:
|
|||
```json
|
||||
{
|
||||
"resolutions": {
|
||||
"@langchain/langgraph-sdk": "^0.0.92",
|
||||
"@langchain/langgraph-sdk": "^0.0.95",
|
||||
"@langchain/core": "^0.3.58"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
"@langchain/core": "^0.3.56",
|
||||
"@langchain/google-genai": "^0.2.9",
|
||||
"@langchain/langgraph": "^0.3.8",
|
||||
"@langchain/langgraph-sdk": "^0.0.92",
|
||||
"@langchain/langgraph-sdk": "^0.0.95",
|
||||
"@langchain/mcp-adapters": "^0.5.2",
|
||||
"@langchain/openai": "^0.5.10",
|
||||
"@mendable/firecrawl-js": "^1.29.1",
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@langchain/langgraph-cli": "latest",
|
||||
"@langchain/langgraph-cli": "^0.0.47",
|
||||
"@tsconfig/recommended": "^1.0.8",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/node": "^22.13.5",
|
||||
|
|
|
|||
257
apps/open-swe/src/__tests__/tokens.test.ts
Normal file
257
apps/open-swe/src/__tests__/tokens.test.ts
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
import { describe, it, expect } from "@jest/globals";
|
||||
import { AIMessage, HumanMessage, ToolMessage } from "@langchain/core/messages";
|
||||
import {
|
||||
calculateConversationHistoryTokenCount,
|
||||
getMessagesSinceLastSummary,
|
||||
MAX_INTERNAL_TOKENS,
|
||||
} from "../utils/tokens.js";
|
||||
|
||||
describe("calculateConversationHistoryTokenCount", () => {
|
||||
it("should return 0 for empty messages array", () => {
|
||||
const result = calculateConversationHistoryTokenCount([]);
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it("should calculate token count for human messages", () => {
|
||||
const messages = [
|
||||
new HumanMessage({
|
||||
content: "This is a test message with exactly 10 words in it.",
|
||||
}),
|
||||
];
|
||||
|
||||
// 10 words, approximately 13 tokens, ~52 characters
|
||||
// Since we estimate 1 token per 4 characters, this should be around 13 tokens
|
||||
const result = calculateConversationHistoryTokenCount(messages);
|
||||
expect(result).toBe(13);
|
||||
});
|
||||
|
||||
it("should calculate token count for AI messages with usage metadata", () => {
|
||||
const messages = [
|
||||
new AIMessage({
|
||||
content: "AI response",
|
||||
usage_metadata: {
|
||||
input_tokens: 10,
|
||||
output_tokens: 10,
|
||||
total_tokens: 20,
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
const result = calculateConversationHistoryTokenCount(messages);
|
||||
expect(result).toBe(20);
|
||||
});
|
||||
|
||||
it("should calculate token count for AI messages without usage metadata", () => {
|
||||
const messages = [
|
||||
new AIMessage({
|
||||
content: "This is an AI response with no usage metadata.",
|
||||
}),
|
||||
];
|
||||
|
||||
// ~12 words, approximately 12 tokens, ~48 characters
|
||||
// Since we estimate 1 token per 4 characters, this should be around 12 tokens
|
||||
const result = calculateConversationHistoryTokenCount(messages);
|
||||
expect(result).toBe(12);
|
||||
});
|
||||
|
||||
it("should calculate token count for AI messages with tool calls", () => {
|
||||
const messages = [
|
||||
new AIMessage({
|
||||
content: "Using a tool",
|
||||
tool_calls: [
|
||||
{
|
||||
name: "calculator",
|
||||
args: { a: 1, b: 2 },
|
||||
},
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
// Content: "Using a tool" (~3 tokens)
|
||||
// Tool name: "calculator" (~2 tokens)
|
||||
// Args: JSON.stringify({a:1,b:2}) (~3 tokens)
|
||||
// Total: ~8 tokens
|
||||
const result = calculateConversationHistoryTokenCount(messages);
|
||||
expect(result).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should calculate token count for tool messages", () => {
|
||||
const messages = [
|
||||
new ToolMessage({
|
||||
content: "Result of tool execution with some data.",
|
||||
tool_call_id: "tool-1",
|
||||
name: "tool",
|
||||
}),
|
||||
];
|
||||
|
||||
// ~8 words, approximately 10 tokens, ~40 characters
|
||||
const result = calculateConversationHistoryTokenCount(messages);
|
||||
expect(result).toBe(10);
|
||||
});
|
||||
|
||||
it("should exclude hidden messages when option is provided", () => {
|
||||
const messages = [
|
||||
new HumanMessage({
|
||||
content: "Visible message",
|
||||
}),
|
||||
new HumanMessage({
|
||||
content: "Hidden message",
|
||||
additional_kwargs: { hidden: true },
|
||||
}),
|
||||
];
|
||||
|
||||
const resultWithoutOption =
|
||||
calculateConversationHistoryTokenCount(messages);
|
||||
const resultWithOption = calculateConversationHistoryTokenCount(messages, {
|
||||
excludeHiddenMessages: true,
|
||||
});
|
||||
|
||||
expect(resultWithoutOption).toBeGreaterThan(resultWithOption);
|
||||
expect(resultWithOption).toBe(4); // "Visible message" is ~4 tokens
|
||||
});
|
||||
|
||||
it("should exclude messages from the end when option is provided", () => {
|
||||
const messages = [
|
||||
new HumanMessage({ content: "First message" }),
|
||||
new HumanMessage({ content: "Second message" }),
|
||||
new HumanMessage({ content: "Third message" }),
|
||||
];
|
||||
|
||||
const resultWithoutOption =
|
||||
calculateConversationHistoryTokenCount(messages);
|
||||
const resultWithOption = calculateConversationHistoryTokenCount(messages, {
|
||||
excludeCountFromEnd: 1,
|
||||
});
|
||||
|
||||
expect(resultWithoutOption).toBeGreaterThan(resultWithOption);
|
||||
// First two messages should be ~7 tokens
|
||||
expect(resultWithOption).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMessagesSinceLastSummary", () => {
|
||||
it("should return all messages when there is no summary message", () => {
|
||||
const messages = [
|
||||
new HumanMessage({ content: "Message 1" }),
|
||||
new AIMessage({ content: "Message 2" }),
|
||||
new HumanMessage({ content: "Message 3" }),
|
||||
];
|
||||
|
||||
const result = getMessagesSinceLastSummary(messages);
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result).toEqual(messages);
|
||||
});
|
||||
|
||||
it("should return messages after the last summary message", () => {
|
||||
const summaryMessage = new AIMessage({
|
||||
content: "Summary of conversation",
|
||||
additional_kwargs: { summary_message: true },
|
||||
});
|
||||
|
||||
const messages = [
|
||||
new HumanMessage({ content: "Message 1" }),
|
||||
summaryMessage,
|
||||
new HumanMessage({ content: "Message 3" }),
|
||||
new AIMessage({ content: "Message 4" }),
|
||||
];
|
||||
|
||||
const result = getMessagesSinceLastSummary(messages);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].content).toBe("Message 3");
|
||||
expect(result[1].content).toBe("Message 4");
|
||||
});
|
||||
|
||||
it("should exclude hidden messages when option is provided", () => {
|
||||
const summaryMessage = new AIMessage({
|
||||
content: "Summary of conversation",
|
||||
additional_kwargs: { summary_message: true },
|
||||
});
|
||||
|
||||
const messages = [
|
||||
summaryMessage,
|
||||
new HumanMessage({ content: "Visible message" }),
|
||||
new HumanMessage({
|
||||
content: "Hidden message",
|
||||
additional_kwargs: { hidden: true },
|
||||
}),
|
||||
new AIMessage({ content: "Another visible message" }),
|
||||
];
|
||||
|
||||
const result = getMessagesSinceLastSummary(messages, {
|
||||
excludeHiddenMessages: true,
|
||||
});
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].content).toBe("Visible message");
|
||||
expect(result[1].content).toBe("Another visible message");
|
||||
});
|
||||
|
||||
it("should exclude messages from the end when option is provided", () => {
|
||||
const summaryMessage = new AIMessage({
|
||||
content: "Summary of conversation",
|
||||
additional_kwargs: { summary_message: true },
|
||||
});
|
||||
|
||||
const messages = [
|
||||
summaryMessage,
|
||||
new HumanMessage({ content: "Message 1" }),
|
||||
new AIMessage({ content: "Message 2" }),
|
||||
new HumanMessage({ content: "Message 3" }),
|
||||
];
|
||||
|
||||
const result = getMessagesSinceLastSummary(messages, {
|
||||
excludeCountFromEnd: 1,
|
||||
});
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].content).toBe("Message 1");
|
||||
expect(result[1].content).toBe("Message 2");
|
||||
});
|
||||
|
||||
it("should handle both excludeHiddenMessages and excludeCountFromEnd options", () => {
|
||||
const summaryMessage = new AIMessage({
|
||||
content: "Summary of conversation",
|
||||
additional_kwargs: { summary_message: true },
|
||||
});
|
||||
|
||||
const messages = [
|
||||
summaryMessage,
|
||||
new HumanMessage({ content: "Message 1" }),
|
||||
new HumanMessage({
|
||||
content: "Hidden message",
|
||||
additional_kwargs: { hidden: true },
|
||||
}),
|
||||
new AIMessage({ content: "Message 3" }),
|
||||
new HumanMessage({ content: "Message 4" }),
|
||||
];
|
||||
|
||||
const result = getMessagesSinceLastSummary(messages, {
|
||||
excludeHiddenMessages: true,
|
||||
excludeCountFromEnd: 1,
|
||||
});
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].content).toBe("Message 1");
|
||||
expect(result[1].content).toBe("Message 3");
|
||||
});
|
||||
|
||||
it("should return empty array if all messages are before the summary", () => {
|
||||
const messages = [
|
||||
new HumanMessage({ content: "Message 1" }),
|
||||
new AIMessage({ content: "Message 2" }),
|
||||
new AIMessage({
|
||||
content: "Summary of conversation",
|
||||
additional_kwargs: { summary_message: true },
|
||||
}),
|
||||
];
|
||||
|
||||
const result = getMessagesSinceLastSummary(messages);
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("MAX_INTERNAL_TOKENS constant", () => {
|
||||
it("should be defined as 60,000", () => {
|
||||
expect(MAX_INTERNAL_TOKENS).toBe(60_000);
|
||||
});
|
||||
});
|
||||
|
|
@ -23,7 +23,7 @@ Your sole objective in this phase is to gather comprehensive context about the c
|
|||
- Use \`search\` tool for all file searches. The \`search\` tool allows for efficient simple and complex searches, and it respect .gitignore patterns.
|
||||
- It's significantly faster results than alternatives like grep or ls -R.
|
||||
- When searching for specific file types, use glob patterns
|
||||
- The pattern field supports both basic strings, and regex
|
||||
- The query field supports both basic strings, and regex
|
||||
- Always use the \`search\` tools instead calling \`grep\` via the \`shell\` tool. You should NEVER call \`grep\` as the same functionality is better provided by \`search\`.
|
||||
- If the user passes a URL, you should use the \`get_url_content\` tool to fetch the contents of the URL.
|
||||
- You should only use this tool to fetch the contents of a URL the user has provided, or that you've discovered during your context searching, which you believe is vital to gathering context for the user's request.
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ You are currently executing a specific task from a pre-generated plan. You have
|
|||
* **Search**: Use \`search\` tool for all file searches. The \`search\` tool allows for efficient simple and complex searches, and it respect .gitignore patterns.
|
||||
* It's significantly faster results than alternatives like grep or ls -R.
|
||||
* When searching for specific file types, use glob patterns
|
||||
* The pattern field supports both basic strings, and regex
|
||||
* The query field supports both basic strings, and regex
|
||||
* **Dependencies**: Use the correct package manager; skip if installation fails
|
||||
* **Pre-commit**: Run \`pre-commit run --files ...\` if .pre-commit-config.yaml exists
|
||||
* **History**: Use \`git log\` and \`git blame\` for additional context when needed
|
||||
|
|
|
|||
|
|
@ -117,6 +117,11 @@ Once you've determined the status of the current task, call either the \`mark_ta
|
|||
|
||||
const totalInternalTokenCount = calculateConversationHistoryTokenCount(
|
||||
state.internalMessages,
|
||||
{
|
||||
// Retain the last 20 messages from state
|
||||
excludeHiddenMessages: true,
|
||||
excludeCountFromEnd: 20,
|
||||
},
|
||||
);
|
||||
|
||||
if (!isCompleted) {
|
||||
|
|
|
|||
|
|
@ -178,6 +178,10 @@ export async function summarizeHistory(
|
|||
const plan = getActivePlanItems(state.taskPlan);
|
||||
const conversationHistoryToSummarize = getMessagesSinceLastSummary(
|
||||
state.internalMessages,
|
||||
{
|
||||
excludeHiddenMessages: true,
|
||||
excludeCountFromEnd: 20,
|
||||
},
|
||||
);
|
||||
|
||||
logger.info(
|
||||
|
|
|
|||
|
|
@ -33,12 +33,14 @@ By reviewing these actions, and comparing them to the plan and original user req
|
|||
3. **Use git commands to gather context**: Below you're provided with a section '<changed_files>', which lists all of the files that were modified/created/deleted in the current branch.
|
||||
- Ensure you use this, paired with commands such as 'git diff {BASE_BRANCH_NAME} <file_path>' to inspect a diff of a file to gather context about the changes made by the Programmer Assistant.
|
||||
|
||||
3. **Gather all of the context necessary**: Ensure you gather all of the context necessary to provide a review of the changes made by the Programmer Assistant.
|
||||
3. **Only search for what is necessary**: Ensure you gather all of the context necessary to provide a review of the changes made by the Programmer Assistant.
|
||||
- Ensure that the actions you perform in this review phase are only the most necessary and targeted actions to gather context.
|
||||
- Avoid rabbit holes for gathering context. You should always first consider whether or not the action you're about to take is necessary to generate a review for the user's request. If it is not, do not take it.
|
||||
|
||||
4. **Leverage \`search\` tool**: Use \`search\` tool for all file searches. The \`search\` tool allows for efficient simple and complex searches, and it respect .gitignore patterns.
|
||||
- It's significantly faster results than alternatives like grep or ls -R.
|
||||
- When searching for specific file types, use glob patterns
|
||||
- The pattern field supports both basic strings, and regex
|
||||
- The query field supports both basic strings, and regex
|
||||
|
||||
5. **Format shell commands precisely**: Ensure all shell commands include proper quoting and escaping. Well-formatted commands prevent errors and provide reliable results.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,43 @@ import { getSandboxWithErrorHandling } from "../../../utils/sandbox.js";
|
|||
import { getRepoAbsolutePath } from "@open-swe/shared/git";
|
||||
import { createLogger, LogLevel } from "../../../utils/logger.js";
|
||||
import { GraphConfig } from "@open-swe/shared/open-swe/types";
|
||||
import { AIMessage, ToolMessage } from "@langchain/core/messages";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { createReviewStartedToolFields } from "@open-swe/shared/open-swe/tools";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "InitializeStateNode");
|
||||
|
||||
function createReviewStartedMessage() {
|
||||
const reviewStartedTool = createReviewStartedToolFields();
|
||||
const toolCallId = uuidv4();
|
||||
const reviewStartedToolCall = {
|
||||
id: toolCallId,
|
||||
name: reviewStartedTool.name,
|
||||
args: {
|
||||
review_started: true,
|
||||
},
|
||||
};
|
||||
|
||||
return [
|
||||
new AIMessage({
|
||||
id: uuidv4(),
|
||||
content: "",
|
||||
additional_kwargs: {
|
||||
hidden: true,
|
||||
},
|
||||
tool_calls: [reviewStartedToolCall],
|
||||
}),
|
||||
new ToolMessage({
|
||||
id: uuidv4(),
|
||||
tool_call_id: toolCallId,
|
||||
content: "",
|
||||
additional_kwargs: {
|
||||
hidden: true,
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
export async function initializeState(
|
||||
state: ReviewerGraphState,
|
||||
config: GraphConfig,
|
||||
|
|
@ -54,6 +88,7 @@ export async function initializeState(
|
|||
return {
|
||||
baseBranchName,
|
||||
changedFiles,
|
||||
messages: createReviewStartedMessage(),
|
||||
...(codebaseTree ? { codebaseTree } : {}),
|
||||
...(dependenciesInstalled !== null ? { dependenciesInstalled } : {}),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,26 +6,43 @@ import {
|
|||
} from "@langchain/core/messages";
|
||||
import { getMessageContentString } from "@open-swe/shared/messages";
|
||||
|
||||
// After 100k tokens, summarize the conversation history.
|
||||
export const MAX_INTERNAL_TOKENS = 100_000;
|
||||
// After 60k tokens, summarize the conversation history.
|
||||
export const MAX_INTERNAL_TOKENS = 60_000;
|
||||
|
||||
export function calculateConversationHistoryTokenCount(
|
||||
messages: BaseMessage[],
|
||||
options?: {
|
||||
excludeHiddenMessages?: boolean;
|
||||
excludeCountFromEnd?: number;
|
||||
},
|
||||
) {
|
||||
let totalChars = 0;
|
||||
messages.forEach((m) => {
|
||||
if (isAIMessage(m)) {
|
||||
const contentString = getMessageContentString(m.content);
|
||||
totalChars += contentString.length;
|
||||
m.tool_calls?.forEach((tc) => {
|
||||
totalChars += tc.name.length;
|
||||
totalChars += JSON.stringify(tc.args).length;
|
||||
});
|
||||
const messagesToCount = options?.excludeCountFromEnd
|
||||
? messages.slice(0, -options.excludeCountFromEnd)
|
||||
: messages;
|
||||
messagesToCount.forEach((m) => {
|
||||
if (options?.excludeHiddenMessages && m.additional_kwargs?.hidden) {
|
||||
return;
|
||||
}
|
||||
if (isHumanMessage(m) || isToolMessage(m)) {
|
||||
const contentString = getMessageContentString(m.content);
|
||||
totalChars += contentString.length;
|
||||
}
|
||||
|
||||
if (isAIMessage(m)) {
|
||||
const usageMetadata = m.usage_metadata;
|
||||
if (usageMetadata) {
|
||||
// multiply by 4 here since we divide by 4 to estimate tokens.
|
||||
totalChars += usageMetadata.total_tokens * 4;
|
||||
} else {
|
||||
const contentString = getMessageContentString(m.content);
|
||||
totalChars += contentString.length;
|
||||
m.tool_calls?.forEach((tc) => {
|
||||
totalChars += tc.name.length;
|
||||
totalChars += JSON.stringify(tc.args).length;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Estimate 1 token for every 4 characters.
|
||||
|
|
@ -34,9 +51,39 @@ export function calculateConversationHistoryTokenCount(
|
|||
|
||||
export function getMessagesSinceLastSummary(
|
||||
messages: BaseMessage[],
|
||||
options?: {
|
||||
excludeHiddenMessages?: boolean;
|
||||
excludeCountFromEnd?: number;
|
||||
},
|
||||
): BaseMessage[] {
|
||||
const allMessagesAfterLastSummary = messages.slice(
|
||||
messages.findIndex((m) => m.additional_kwargs?.summary_message),
|
||||
// Find the index of the last summary message
|
||||
const lastSummaryIndex = messages.findIndex(
|
||||
(m) => m.additional_kwargs?.summary_message,
|
||||
);
|
||||
return allMessagesAfterLastSummary;
|
||||
|
||||
// Get all messages after the last summary message
|
||||
let messagesAfterLastSummary =
|
||||
lastSummaryIndex >= 0
|
||||
? messages.slice(lastSummaryIndex + 1)
|
||||
: [...messages];
|
||||
|
||||
// Apply excludeHiddenMessages option if provided
|
||||
if (options?.excludeHiddenMessages) {
|
||||
messagesAfterLastSummary = messagesAfterLastSummary.filter(
|
||||
(m) => !m.additional_kwargs?.hidden,
|
||||
);
|
||||
}
|
||||
|
||||
// Apply excludeCountFromEnd option if provided
|
||||
if (options?.excludeCountFromEnd && options.excludeCountFromEnd > 0) {
|
||||
messagesAfterLastSummary = messagesAfterLastSummary.slice(
|
||||
0,
|
||||
Math.max(
|
||||
0,
|
||||
messagesAfterLastSummary.length - options.excludeCountFromEnd,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return messagesAfterLastSummary;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
"dependencies": {
|
||||
"@langchain/core": "^0.3.57",
|
||||
"@langchain/langgraph": "^0.3.8",
|
||||
"@langchain/langgraph-sdk": "^0.0.92",
|
||||
"@langchain/langgraph-sdk": "^0.0.95",
|
||||
"@octokit/app": "^16.0.1",
|
||||
"@open-swe/shared": "*",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { ThemeToggle } from "@/components/theme-toggle";
|
|||
import { InstallationSelector } from "@/components/github/installation-selector";
|
||||
import { GitHubAppProvider } from "@/providers/GitHubApp";
|
||||
import { MANAGER_GRAPH_ID } from "@open-swe/shared/constants";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type FilterStatus = "all" | "running" | "completed" | "failed" | "pending";
|
||||
|
||||
|
|
@ -120,11 +121,12 @@ function AllThreadsPageContent() {
|
|||
key={status}
|
||||
variant={statusFilter === status ? "secondary" : "ghost"}
|
||||
size="sm"
|
||||
className={`h-7 text-xs ${
|
||||
className={cn(
|
||||
"h-7 text-xs",
|
||||
statusFilter === status
|
||||
? "bg-muted text-foreground dark:bg-gray-700"
|
||||
: "text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||
}`}
|
||||
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||
)}
|
||||
onClick={() => setStatusFilter(status)}
|
||||
>
|
||||
{status === "all"
|
||||
|
|
|
|||
|
|
@ -352,11 +352,11 @@ function ActionItem(props: ActionItemProps) {
|
|||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="text-foreground/80 text-xs font-normal">
|
||||
{castProps.pattern}
|
||||
{castProps.query}
|
||||
</code>
|
||||
<div className="bg-border h-4 w-[1px] dark:bg-white"></div>
|
||||
<MatchCaseIcon matchCase={!!castProps.case_sensitive} />
|
||||
{castProps.regex && (
|
||||
{!castProps.match_string && (
|
||||
<span className="text-muted-foreground bg-muted/50 rounded px-1 text-xs font-normal">
|
||||
regex
|
||||
</span>
|
||||
|
|
|
|||
51
apps/web/src/components/gen-ui/code-review-started.tsx
Normal file
51
apps/web/src/components/gen-ui/code-review-started.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
"use client";
|
||||
|
||||
import { Sparkles } from "lucide-react";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Check } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type CodeReviewStartedProps = {
|
||||
status?: "generating" | "done";
|
||||
};
|
||||
|
||||
export function CodeReviewStarted({ status = "done" }: CodeReviewStartedProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group via-background to-background dark:via-background dark:to-background rounded-xl border bg-gradient-to-br from-blue-50/50 transition-shadow dark:from-blue-950/20",
|
||||
"shadow-sm hover:shadow-md",
|
||||
)}
|
||||
>
|
||||
{/* Header */}
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex items-center bg-gradient-to-r from-blue-50 to-blue-50/50 p-4 backdrop-blur-sm dark:from-blue-950/30 dark:to-blue-950/10",
|
||||
"rounded-xl",
|
||||
)}
|
||||
>
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-500 shadow-md dark:bg-blue-600">
|
||||
<Sparkles className="h-4 w-4 text-white" />
|
||||
</div>
|
||||
|
||||
<div className="ml-3 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-foreground text-sm font-semibold">
|
||||
Code review started
|
||||
</h3>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="border-blue-200 bg-blue-100 text-blue-700 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-300"
|
||||
>
|
||||
<Check className="h-3 w-3" />
|
||||
In Progress
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-muted-foreground mt-1 text-xs">
|
||||
Analyzing code for best practices and potential improvements
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
FileText,
|
||||
} from "lucide-react";
|
||||
import { BasicMarkdownText } from "../thread/markdown-text";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type MarkTaskCompletedProps = {
|
||||
status: "loading" | "generating" | "done";
|
||||
|
|
@ -77,9 +78,10 @@ export function MarkTaskCompleted({
|
|||
)}
|
||||
|
||||
<div
|
||||
className={`flex items-center border-b border-green-200 bg-green-50 p-2 dark:border-green-800 dark:bg-green-900/50 ${
|
||||
status === "done" && review ? "cursor-pointer" : ""
|
||||
}`}
|
||||
className={cn(
|
||||
"flex items-center border-b border-green-200 bg-green-50 p-2 dark:border-green-800 dark:bg-green-900/50",
|
||||
status === "done" && review ? "cursor-pointer" : "",
|
||||
)}
|
||||
onClick={
|
||||
status === "done" && review
|
||||
? () => setExpanded((prev) => !prev)
|
||||
|
|
@ -200,11 +202,12 @@ export function MarkTaskIncomplete({
|
|||
)}
|
||||
|
||||
<div
|
||||
className={`flex items-center border-b border-red-200 bg-red-50 p-2 dark:border-red-800 dark:bg-red-900/50 ${
|
||||
className={cn(
|
||||
"flex items-center border-b border-red-200 bg-red-50 p-2 dark:border-red-800 dark:bg-red-900/50",
|
||||
status === "done" && (review || additionalActions)
|
||||
? "cursor-pointer"
|
||||
: ""
|
||||
}`}
|
||||
: "",
|
||||
)}
|
||||
onClick={
|
||||
status === "done" && (review || additionalActions)
|
||||
? () => setExpanded((prev) => !prev)
|
||||
|
|
|
|||
|
|
@ -74,19 +74,21 @@ export function TaskSummary({
|
|||
|
||||
{summaryText && status === "done" && (
|
||||
<div
|
||||
className={`border-t p-2 ${
|
||||
className={cn(
|
||||
"border-t p-2",
|
||||
completed === false
|
||||
? "border-amber-300 bg-amber-100/50 dark:border-amber-800 dark:bg-amber-900/50"
|
||||
: "border-green-300 bg-green-100/50 dark:border-green-800 dark:bg-green-900/50"
|
||||
}`}
|
||||
: "border-green-300 bg-green-100/50 dark:border-green-800 dark:bg-green-900/50",
|
||||
)}
|
||||
>
|
||||
<button
|
||||
onClick={() => setShowSummary(!showSummary)}
|
||||
className={`flex cursor-pointer items-center gap-1 text-xs font-normal ${
|
||||
className={cn(
|
||||
"flex cursor-pointer items-center gap-1 text-xs font-normal",
|
||||
completed === false
|
||||
? "text-amber-600 hover:text-amber-700 dark:text-amber-400 dark:hover:text-amber-300"
|
||||
: "text-green-600 hover:text-green-700 dark:text-green-400 dark:hover:text-green-300"
|
||||
}`}
|
||||
: "text-green-600 hover:text-green-700 dark:text-green-400 dark:hover:text-green-300",
|
||||
)}
|
||||
>
|
||||
<FileText className="h-3 w-3" />
|
||||
{showSummary ? "Hide summary" : "Show summary"}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useGitHubAppProvider } from "@/providers/GitHubApp";
|
|||
import { InstallationPrompt } from "./installation-prompt";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface RepositoryListProps {
|
||||
className?: string;
|
||||
|
|
@ -14,7 +15,7 @@ export function RepositoryList({ className = "" }: RepositoryListProps) {
|
|||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className={`p-4 ${className}`}>
|
||||
<div className={cn("p-4", className)}>
|
||||
<div className="mb-4 h-6 w-24 animate-pulse rounded bg-gray-200"></div>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
|
|
@ -31,7 +32,7 @@ export function RepositoryList({ className = "" }: RepositoryListProps) {
|
|||
|
||||
if (error) {
|
||||
return (
|
||||
<div className={`p-4 ${className}`}>
|
||||
<div className={cn("p-4", className)}>
|
||||
<div className="mb-4 rounded-md border border-red-200 bg-red-50 p-4">
|
||||
<p className="text-sm text-red-800">{error}</p>
|
||||
</div>
|
||||
|
|
@ -47,14 +48,14 @@ export function RepositoryList({ className = "" }: RepositoryListProps) {
|
|||
|
||||
if (!isInstalled) {
|
||||
return (
|
||||
<div className={`p-4 ${className}`}>
|
||||
<div className={cn("p-4", className)}>
|
||||
<InstallationPrompt />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`p-4 ${className}`}>
|
||||
<div className={cn("p-4", className)}>
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h2 className="text-lg font-medium">Your GitHub Repositories</h2>
|
||||
<Button
|
||||
|
|
@ -107,7 +108,12 @@ export function RepositoryList({ className = "" }: RepositoryListProps) {
|
|||
</p>
|
||||
<div className="mt-2 flex items-center text-xs">
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 ${repo.private ? "bg-gray-100" : "bg-green-100 text-green-800"} mr-2`}
|
||||
className={cn(
|
||||
"rounded-full px-2 py-0.5",
|
||||
repo.private
|
||||
? "bg-gray-100"
|
||||
: "bg-green-100 text-green-800",
|
||||
)}
|
||||
>
|
||||
{repo.private ? "Private" : "Public"}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from "@/components/gen-ui/task-review";
|
||||
import { DiagnoseErrorAction } from "@/components/v2/diagnose-error-action";
|
||||
import { WriteTechnicalNotes } from "@/components/gen-ui/write-technical-notes";
|
||||
import { CodeReviewStarted } from "@/components/gen-ui/code-review-started";
|
||||
import { ToolCall } from "@langchain/core/messages/tool";
|
||||
import {
|
||||
createApplyPatchToolFields,
|
||||
|
|
@ -43,6 +44,7 @@ import {
|
|||
createGetURLContentToolFields,
|
||||
createWriteTechnicalNotesToolFields,
|
||||
createConversationHistorySummaryToolFields,
|
||||
createReviewStartedToolFields,
|
||||
} from "@open-swe/shared/open-swe/tools";
|
||||
import { z } from "zod";
|
||||
import { isAIMessageSDK, isToolMessageSDK } from "@/lib/langchain-messages";
|
||||
|
|
@ -61,6 +63,8 @@ const markTaskNotCompletedTool = createMarkTaskNotCompletedToolFields();
|
|||
type MarkTaskNotCompletedToolArgs = z.infer<
|
||||
typeof markTaskNotCompletedTool.schema
|
||||
>;
|
||||
const reviewStartedTool = createReviewStartedToolFields();
|
||||
type ReviewStartedToolArgs = z.infer<typeof reviewStartedTool.schema>;
|
||||
const searchTool = createSearchToolFields(dummyRepo);
|
||||
type SearchToolArgs = z.infer<typeof searchTool.schema>;
|
||||
const openPrTool = createOpenPrToolFields();
|
||||
|
|
@ -200,8 +204,8 @@ export function mapToolMessageToActionStepProps(
|
|||
actionType: "search",
|
||||
status,
|
||||
success,
|
||||
pattern: args.pattern || "",
|
||||
regex: args.regex || false,
|
||||
query: args.query || "",
|
||||
match_string: args.match_string || false,
|
||||
case_sensitive: args.case_sensitive || false,
|
||||
context_lines: args.context_lines || 0,
|
||||
max_results: args.max_results || 0,
|
||||
|
|
@ -349,6 +353,10 @@ export function AssistantMessage({
|
|||
? aiToolCalls.find((tc) => tc.name === conversationHistorySummaryTool.name)
|
||||
: undefined;
|
||||
|
||||
const reviewStartedToolCall = message
|
||||
? aiToolCalls.find((tc) => tc.name === reviewStartedTool.name)
|
||||
: undefined;
|
||||
|
||||
// Check if this is a conversation history summary message
|
||||
if (conversationHistorySummaryToolCall && aiToolCalls.length === 1) {
|
||||
const args =
|
||||
|
|
@ -363,6 +371,21 @@ export function AssistantMessage({
|
|||
);
|
||||
}
|
||||
|
||||
// Check if this is a review started message
|
||||
if (reviewStartedToolCall && aiToolCalls.length === 1) {
|
||||
const correspondingToolResult = toolResults.find(
|
||||
(tr) => tr && tr.tool_call_id === reviewStartedToolCall.id,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<CodeReviewStarted
|
||||
status={correspondingToolResult ? "done" : "generating"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// We can be sure that if either task status tool call is present, it will be the
|
||||
// only tool call/result we need to render for this message.
|
||||
if (markTaskCompletedToolCall || markTaskNotCompletedToolCall) {
|
||||
|
|
@ -550,8 +573,8 @@ export function AssistantMessage({
|
|||
return {
|
||||
actionType: "search",
|
||||
status: "generating",
|
||||
pattern: args?.pattern || "",
|
||||
regex: args?.regex || false,
|
||||
query: args?.query || "",
|
||||
match_string: args?.match_string || false,
|
||||
case_sensitive: args?.case_sensitive || false,
|
||||
context_lines: args?.context_lines || 0,
|
||||
max_results: args?.max_results || 0,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import {
|
|||
import {
|
||||
DO_NOT_RENDER_ID_PREFIX,
|
||||
PLANNER_GRAPH_ID,
|
||||
PROGRAMMER_GRAPH_ID,
|
||||
} from "@open-swe/shared/constants";
|
||||
import { Message } from "@langchain/langgraph-sdk";
|
||||
import { InitializeStep } from "../gen-ui/initialize-step";
|
||||
|
|
@ -158,6 +157,7 @@ function isNodeEndCommandUpdate(data: unknown): data is {
|
|||
}
|
||||
|
||||
const REVIEWER_NODE_IDS = [
|
||||
"initialize-state",
|
||||
"generate-review-actions",
|
||||
"take-review-actions",
|
||||
"diagnose-reviewer-error",
|
||||
|
|
@ -205,11 +205,9 @@ export function ActionsRenderer<State extends PlannerGraphState | GraphState>({
|
|||
if (isNodeEndCommandUpdate(data.data)) {
|
||||
const outputMessages = data.data.output.update
|
||||
.messages as unknown as Message[];
|
||||
console.log("outputMessages", outputMessages);
|
||||
debouncedSetMessages(outputMessages);
|
||||
} else if (isNodeEndMessagesUpdate(data.data)) {
|
||||
const outputMessages = data.data.output.messages;
|
||||
console.log("outputMessages", outputMessages);
|
||||
debouncedSetMessages(outputMessages);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { useRouter } from "next/navigation";
|
|||
import { Badge } from "../ui/badge";
|
||||
import { Button } from "../ui/button";
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function ThreadCard({ thread }: { thread: ThreadDisplayInfo }) {
|
||||
const router = useRouter();
|
||||
|
|
@ -82,7 +83,7 @@ export function ThreadCard({ thread }: { thread: ThreadDisplayInfo }) {
|
|||
</div>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={`${getStatusColor(thread.status)} text-xs`}
|
||||
className={cn(getStatusColor(thread.status), "text-xs")}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
{getStatusIcon(thread.status)}
|
||||
|
|
@ -122,7 +123,10 @@ export function ThreadCard({ thread }: { thread: ThreadDisplayInfo }) {
|
|||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={`h-5 w-5 p-0 hover:text-gray-300 ${getPRStatusColor(thread.pullRequest.status)}`}
|
||||
className={cn(
|
||||
"h-5 w-5 p-0 hover:text-gray-300",
|
||||
getPRStatusColor(thread.pullRequest.status),
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
window.open(thread.pullRequest!.url, "_blank");
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from "../../utils/scroll-utils";
|
||||
import { ManagerChat } from "./manager-chat";
|
||||
import { CancelStreamButton } from "./cancel-stream-button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface ThreadViewProps {
|
||||
stream: ReturnType<typeof useStream<ManagerGraphState>>;
|
||||
|
|
@ -99,13 +100,14 @@ export function ThreadView({
|
|||
</Button>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2">
|
||||
<div
|
||||
className={`size-2 flex-shrink-0 rounded-full ${
|
||||
className={cn(
|
||||
"size-2 flex-shrink-0 rounded-full",
|
||||
displayThread.status === "running"
|
||||
? "bg-blue-500"
|
||||
: displayThread.status === "completed"
|
||||
? "bg-green-500"
|
||||
: "bg-red-500"
|
||||
}`}
|
||||
: "bg-red-500",
|
||||
)}
|
||||
></div>
|
||||
<span className="text-muted-foreground max-w-[500px] truncate font-mono text-sm">
|
||||
{displayThread.title}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
"typescript": "^5"
|
||||
},
|
||||
"resolutions": {
|
||||
"@langchain/langgraph-sdk": "^0.0.92",
|
||||
"@langchain/langgraph-sdk": "^0.0.95",
|
||||
"@langchain/core": "^0.3.58"
|
||||
},
|
||||
"packageManager": "yarn@3.5.1"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
"dependencies": {
|
||||
"@langchain/core": "^0.3.56",
|
||||
"@langchain/langgraph": "^0.3.8",
|
||||
"@langchain/langgraph-sdk": "^0.0.92",
|
||||
"@langchain/langgraph-sdk": "^0.0.95",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
"zod": "^3.25.32"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -109,15 +109,18 @@ export function createUpdatePlanToolFields() {
|
|||
export function createSearchToolFields(targetRepository: TargetRepository) {
|
||||
const repoRoot = getRepoAbsolutePath(targetRepository);
|
||||
const searchSchema = z.object({
|
||||
pattern: z
|
||||
query: z
|
||||
.string()
|
||||
.describe("The string or regex to search the codebase for."),
|
||||
regex: z
|
||||
.describe(
|
||||
"The string or regex to search the codebase for. If passing a plain string, ensure to also set the 'match_string' field to true. If passing a regex, ensure to also set the 'match_string' field to false.",
|
||||
),
|
||||
|
||||
match_string: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe(
|
||||
"Whether or not to treat the pattern as a regex. Defaults to false.",
|
||||
"Whether or not to treat the query as a fixed string to search for. If true, it will search for results which match the query exactly. If false, the query will be treated as a regex. Defaults to false.",
|
||||
),
|
||||
|
||||
case_sensitive: z
|
||||
|
|
@ -165,7 +168,7 @@ export function createSearchToolFields(targetRepository: TargetRepository) {
|
|||
return {
|
||||
name: "search",
|
||||
schema: searchSchema,
|
||||
description: `Execute a search in the repository. The working directory this command will be executed in is \`${repoRoot}\`.`,
|
||||
description: `Execute a search in the repository. Should be used to search for content via string matching or regex in the codebase. The working directory this command will be executed in is \`${repoRoot}\`.`,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +206,7 @@ export function formatSearchCommand(
|
|||
}
|
||||
|
||||
// Regex vs fixed string
|
||||
if (!cmd.regex) {
|
||||
if (cmd.match_string) {
|
||||
args.push("--fixed-strings");
|
||||
}
|
||||
|
||||
|
|
@ -212,19 +215,25 @@ export function formatSearchCommand(
|
|||
args.push(`-C`, String(cmd.context_lines));
|
||||
}
|
||||
|
||||
// File globs
|
||||
// File globs - use ripgrep's glob handling instead of shell expansion
|
||||
if (cmd.include_files) {
|
||||
args.push("--glob", cmd.include_files);
|
||||
// Quote the glob pattern to prevent shell interpretation
|
||||
args.push("--glob", escapeShellArg(cmd.include_files));
|
||||
}
|
||||
|
||||
if (cmd.exclude_files) {
|
||||
args.push("--glob", `!${cmd.exclude_files}`);
|
||||
// Quote the exclude pattern to prevent shell interpretation
|
||||
args.push("--glob", escapeShellArg(`!${cmd.exclude_files}`));
|
||||
}
|
||||
|
||||
// File types
|
||||
if (cmd.file_types && cmd.file_types.length > 0) {
|
||||
// Process each file type individually to avoid glob expansion issues
|
||||
for (const ext of cmd.file_types) {
|
||||
args.push("--glob", `**/*${ext}`);
|
||||
// Normalize extension format (ensure it has a leading dot)
|
||||
const normalizedExt = ext.startsWith(".") ? ext : `.${ext}`;
|
||||
// Quote the glob pattern to prevent shell interpretation
|
||||
args.push("--glob", escapeShellArg(`**/*${normalizedExt}`));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -238,9 +247,10 @@ export function formatSearchCommand(
|
|||
args.push("--max-count", String(cmd.max_results));
|
||||
}
|
||||
|
||||
// The pattern (must come after flags)
|
||||
if (cmd.pattern) {
|
||||
args.push(escapeShellArg(cmd.pattern));
|
||||
// The query (must come after path for ripgrep to interpret it correctly)
|
||||
if (cmd.query) {
|
||||
// Double-quote the pattern to ensure it's treated as a pattern and not a path
|
||||
args.push(escapeShellArg(cmd.query));
|
||||
}
|
||||
|
||||
return args;
|
||||
|
|
@ -449,3 +459,16 @@ export function createCodeReviewMarkTaskNotCompleteFields() {
|
|||
"Use this tool to mark a task as not complete. This should be called if you determine that the task has not been successfully completed, and you have additional tasks the programmer should take to successfully complete the task.",
|
||||
};
|
||||
}
|
||||
|
||||
export function createReviewStartedToolFields() {
|
||||
const reviewStartedSchema = z.object({
|
||||
review_started: z.boolean(),
|
||||
});
|
||||
|
||||
return {
|
||||
name: "review_started",
|
||||
description:
|
||||
"<not used as an actual tool call. only used as shared types between the client and agent>",
|
||||
schema: reviewStartedSchema,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
44
yarn.lock
44
yarn.lock
|
|
@ -2483,14 +2483,14 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@langchain/langgraph-api@npm:0.0.45":
|
||||
version: 0.0.45
|
||||
resolution: "@langchain/langgraph-api@npm:0.0.45"
|
||||
"@langchain/langgraph-api@npm:0.0.47":
|
||||
version: 0.0.47
|
||||
resolution: "@langchain/langgraph-api@npm:0.0.47"
|
||||
dependencies:
|
||||
"@babel/code-frame": ^7.26.2
|
||||
"@hono/node-server": ^1.12.0
|
||||
"@hono/zod-validator": ^0.2.2
|
||||
"@langchain/langgraph-ui": 0.0.45
|
||||
"@langchain/langgraph-ui": 0.0.47
|
||||
"@types/json-schema": ^7.0.15
|
||||
"@typescript/vfs": ^1.6.0
|
||||
dedent: ^1.5.3
|
||||
|
|
@ -2516,7 +2516,7 @@ __metadata:
|
|||
peerDependenciesMeta:
|
||||
"@langchain/langgraph-sdk":
|
||||
optional: true
|
||||
checksum: ceddae262fe3e9064820dfb0faf53af771c29eb9b7e0a1862e2982666dadcc5e329ce0c286af171315135c963c5c56c4bd49c519edb745c8a1c40ab1187081c5
|
||||
checksum: 9d2ff14cc9fc360e200ea9ad134701593f2f717f49409155d7b3e42f8dd3cdda4736a75a31ffe4be4d18cd4b5cce1673098778d338a50dcf75d44ddc88a7e802
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -2531,13 +2531,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@langchain/langgraph-cli@npm:latest":
|
||||
version: 0.0.45
|
||||
resolution: "@langchain/langgraph-cli@npm:0.0.45"
|
||||
"@langchain/langgraph-cli@npm:^0.0.47":
|
||||
version: 0.0.47
|
||||
resolution: "@langchain/langgraph-cli@npm:0.0.47"
|
||||
dependencies:
|
||||
"@babel/code-frame": ^7.26.2
|
||||
"@commander-js/extra-typings": ^13.0.0
|
||||
"@langchain/langgraph-api": 0.0.45
|
||||
"@langchain/langgraph-api": 0.0.47
|
||||
chokidar: ^4.0.3
|
||||
commander: ^13.0.0
|
||||
dedent: ^1.5.3
|
||||
|
|
@ -2555,13 +2555,13 @@ __metadata:
|
|||
zod: ^3.23.8
|
||||
bin:
|
||||
langgraphjs: dist/cli/cli.mjs
|
||||
checksum: 5136009dbf8b2ba078be7dfe421f3a9d9b1c86ac52503e26d2d1b667ab8fea677f9596da5f5fced4191c1188861390ed11d2329c907cdb917fb86dc274d361ec
|
||||
checksum: 159bac73345e98821e84c415faabb3e936b304ca51967017db4acd29090b9a631ca5bf3129a50caf21e19a57e8886144a16571f7e7049864414abaacb625d563
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@langchain/langgraph-sdk@npm:^0.0.92":
|
||||
version: 0.0.92
|
||||
resolution: "@langchain/langgraph-sdk@npm:0.0.92"
|
||||
"@langchain/langgraph-sdk@npm:^0.0.95":
|
||||
version: 0.0.95
|
||||
resolution: "@langchain/langgraph-sdk@npm:0.0.95"
|
||||
dependencies:
|
||||
"@types/json-schema": ^7.0.15
|
||||
p-queue: ^6.6.2
|
||||
|
|
@ -2578,13 +2578,13 @@ __metadata:
|
|||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
checksum: 905380f0785da27dd5638b37e6215f3f2655cd0cb9111f6178e4b72085e930f1e250dfbfa6bb3b0ce4b7dc7f129f988095fac2240b420c09f63d65c3a8d5b8d6
|
||||
checksum: a8eaf39a3f69bf7fd3645bdaec4869ced7916427669cf1c8480dc8ee4efbc42b2c6431deedd41d02f977de3305832713d3bb894307246f15ba06562fb8384bd3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@langchain/langgraph-ui@npm:0.0.45":
|
||||
version: 0.0.45
|
||||
resolution: "@langchain/langgraph-ui@npm:0.0.45"
|
||||
"@langchain/langgraph-ui@npm:0.0.47":
|
||||
version: 0.0.47
|
||||
resolution: "@langchain/langgraph-ui@npm:0.0.47"
|
||||
dependencies:
|
||||
"@commander-js/extra-typings": ^13.0.0
|
||||
commander: ^13.0.0
|
||||
|
|
@ -2593,7 +2593,7 @@ __metadata:
|
|||
zod: ^3.23.8
|
||||
bin:
|
||||
langgraphjs-ui: ./dist/cli.mjs
|
||||
checksum: e48c0aa52f898765a240f185318fe18668594a67eb0c059b99adf22e46c9f4a3a217c6696533df6c755863ff2c79925753bd17e5c98d73a865e84f933db81939
|
||||
checksum: cdacbba249ac6ce6ea07104b57c0c73bea7332be3d7a2f63b63b386bf5d1a4112acf7a3adc53b64fe7f10999756879b5aac90c6c8ad91bb05e0cd57e36710afc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -3462,8 +3462,8 @@ __metadata:
|
|||
"@langchain/core": ^0.3.56
|
||||
"@langchain/google-genai": ^0.2.9
|
||||
"@langchain/langgraph": ^0.3.8
|
||||
"@langchain/langgraph-cli": latest
|
||||
"@langchain/langgraph-sdk": ^0.0.92
|
||||
"@langchain/langgraph-cli": ^0.0.47
|
||||
"@langchain/langgraph-sdk": ^0.0.95
|
||||
"@langchain/mcp-adapters": ^0.5.2
|
||||
"@langchain/openai": ^0.5.10
|
||||
"@mendable/firecrawl-js": ^1.29.1
|
||||
|
|
@ -3513,7 +3513,7 @@ __metadata:
|
|||
"@eslint/js": ^9.19.0
|
||||
"@langchain/core": ^0.3.56
|
||||
"@langchain/langgraph": ^0.3.8
|
||||
"@langchain/langgraph-sdk": ^0.0.92
|
||||
"@langchain/langgraph-sdk": ^0.0.95
|
||||
"@octokit/rest": ^22.0.0
|
||||
"@octokit/types": ^12.0.0
|
||||
"@tsconfig/recommended": ^1.0.8
|
||||
|
|
@ -3538,7 +3538,7 @@ __metadata:
|
|||
"@eslint/js": ^9.19.0
|
||||
"@langchain/core": ^0.3.57
|
||||
"@langchain/langgraph": ^0.3.8
|
||||
"@langchain/langgraph-sdk": ^0.0.92
|
||||
"@langchain/langgraph-sdk": ^0.0.95
|
||||
"@octokit/app": ^16.0.1
|
||||
"@octokit/types": ^14.1.0
|
||||
"@open-swe/shared": "*"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue