Update
This commit is contained in:
+1
-12
@@ -2,13 +2,6 @@
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
@@ -29,10 +22,6 @@ next-env.d.ts
|
||||
|
||||
# logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# caches
|
||||
.cache/
|
||||
@@ -56,4 +45,4 @@ Thumbs.db
|
||||
|
||||
# local notes
|
||||
CLAUDE.md
|
||||
AGENTS.md
|
||||
AGENTS.md
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"config": "tailwind.config.mjs",
|
||||
"css": "src/app/globals.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true,
|
||||
@@ -15,6 +15,6 @@
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
"hooks": "@/app/hooks"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-24
@@ -1,30 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
// EvoScientist `EvoSci deploy` backend (langgraph dev), default 127.0.0.1:6174.
|
||||
// Override with LANGGRAPH_API_URL if you used `EvoSci deploy --port <other>`.
|
||||
const LANGGRAPH_TARGET =
|
||||
process.env.LANGGRAPH_API_URL || "http://127.0.0.1:6174";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
// Allow loading the dev UI (and its HMR resources) from these LAN hosts,
|
||||
// e.g. opening http://192.168.0.59:3000 from a phone on the same Wi-Fi.
|
||||
// Add/replace with your Mac's LAN IP (see `ipconfig getifaddr en0`).
|
||||
allowedDevOrigins: ["192.168.0.59"],
|
||||
|
||||
// Same-origin reverse proxy to the EvoScientist backend. The browser talks
|
||||
// only to this Next server; Next (running on the Mac) forwards to the
|
||||
// backend on 127.0.0.1. This lets a phone reach the backend WITHOUT the
|
||||
// EvoScientist repo binding to 0.0.0.0, and avoids cross-origin/CORS issues.
|
||||
// In the UI's config dialog, set Deployment URL to: <this-origin>/lg
|
||||
// Dev-only: rewrites don't apply to a future static export build.
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: "/lg/:path*",
|
||||
destination: `${LANGGRAPH_TARGET}/:path*`,
|
||||
},
|
||||
];
|
||||
},
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
Generated
-32
@@ -10,7 +10,6 @@
|
||||
"dependencies": {
|
||||
"@langchain/core": "1.1.19",
|
||||
"@langchain/langgraph-sdk": "1.0.3",
|
||||
"@radix-ui/colors": "^1.0.0",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-scroll-area": "^1.2.9",
|
||||
@@ -38,8 +37,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9",
|
||||
"@headlessui/tailwindcss": "^0.2.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/node": "^20",
|
||||
@@ -555,19 +552,6 @@
|
||||
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@headlessui/tailwindcss": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/tailwindcss/-/tailwindcss-0.2.2.tgz",
|
||||
"integrity": "sha512-xNe42KjdyA4kfUKLLPGzME9zkH7Q3rOZ5huFihWNWOQFxnItxPB3/67yBI8/qBfY8nwBRx5GHn4VprsoluVMGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": "^3.0 || ^4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
|
||||
@@ -1495,12 +1479,6 @@
|
||||
"node": ">=12.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/colors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-1.0.1.tgz",
|
||||
"integrity": "sha512-xySw8f0ZVsAEP+e7iLl3EvcBXX7gsIlC1Zso/sPBW9gIWerBTgz6axrjU+MZ39wD+WFi5h5zdWpsg3+hwt2Qsg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/number": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
|
||||
@@ -2222,16 +2200,6 @@
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/container-queries": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/container-queries/-/container-queries-0.1.1.tgz",
|
||||
"integrity": "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/forms": {
|
||||
"version": "0.5.11",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.11.tgz",
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"dependencies": {
|
||||
"@langchain/core": "1.1.19",
|
||||
"@langchain/langgraph-sdk": "1.0.3",
|
||||
"@radix-ui/colors": "^1.0.0",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-scroll-area": "^1.2.9",
|
||||
@@ -44,8 +43,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9",
|
||||
"@headlessui/tailwindcss": "^0.2.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/node": "^20",
|
||||
|
||||
@@ -12,13 +12,13 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { DEFAULT_ASSISTANT_ID, StandaloneConfig } from "@/lib/config";
|
||||
import { DEFAULT_ASSISTANT_ID, DeploymentConfig } from "@/lib/config";
|
||||
|
||||
interface ConfigDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onSave: (config: StandaloneConfig) => void;
|
||||
initialConfig?: StandaloneConfig;
|
||||
onSave: (config: DeploymentConfig) => void;
|
||||
initialConfig?: DeploymentConfig;
|
||||
}
|
||||
|
||||
export function ConfigDialog({
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import {
|
||||
type Message,
|
||||
type Assistant,
|
||||
type Checkpoint,
|
||||
} from "@langchain/langgraph-sdk";
|
||||
import { type Message, type Assistant } from "@langchain/langgraph-sdk";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import type { UseStreamThread } from "@langchain/langgraph-sdk/react";
|
||||
import type { TodoItem } from "@/app/types/types";
|
||||
@@ -71,34 +67,6 @@ export function useChat({
|
||||
[stream, activeAssistant?.config, onHistoryRevalidate]
|
||||
);
|
||||
|
||||
const runSingleStep = useCallback(
|
||||
(
|
||||
messages: Message[],
|
||||
checkpoint?: Checkpoint,
|
||||
isRerunningSubagent?: boolean,
|
||||
optimisticMessages?: Message[]
|
||||
) => {
|
||||
if (checkpoint) {
|
||||
stream.submit(undefined, {
|
||||
...(optimisticMessages
|
||||
? { optimisticValues: { messages: optimisticMessages } }
|
||||
: {}),
|
||||
config: activeAssistant?.config,
|
||||
checkpoint: checkpoint,
|
||||
...(isRerunningSubagent
|
||||
? { interruptAfter: ["tools"] }
|
||||
: { interruptBefore: ["tools"] }),
|
||||
});
|
||||
} else {
|
||||
stream.submit(
|
||||
{ messages },
|
||||
{ config: activeAssistant?.config, interruptBefore: ["tools"] }
|
||||
);
|
||||
}
|
||||
},
|
||||
[stream, activeAssistant?.config]
|
||||
);
|
||||
|
||||
const setFiles = useCallback(
|
||||
async (files: Record<string, string>) => {
|
||||
if (!threadId) return;
|
||||
@@ -109,29 +77,6 @@ export function useChat({
|
||||
[client, threadId]
|
||||
);
|
||||
|
||||
const continueStream = useCallback(
|
||||
(hasTaskToolCall?: boolean) => {
|
||||
stream.submit(undefined, {
|
||||
config: {
|
||||
...(activeAssistant?.config || {}),
|
||||
recursion_limit: 100,
|
||||
},
|
||||
...(hasTaskToolCall
|
||||
? { interruptAfter: ["tools"] }
|
||||
: { interruptBefore: ["tools"] }),
|
||||
});
|
||||
// Update thread list when continuing stream
|
||||
onHistoryRevalidate?.();
|
||||
},
|
||||
[stream, activeAssistant?.config, onHistoryRevalidate]
|
||||
);
|
||||
|
||||
const markCurrentThreadAsResolved = useCallback(() => {
|
||||
stream.submit(null, { command: { goto: "__end__", update: null } });
|
||||
// Update thread list when marking thread as resolved
|
||||
onHistoryRevalidate?.();
|
||||
}, [stream, onHistoryRevalidate]);
|
||||
|
||||
const resumeInterrupt = useCallback(
|
||||
(value: any) => {
|
||||
stream.submit(null, { command: { resume: value } });
|
||||
@@ -156,12 +101,8 @@ export function useChat({
|
||||
isLoading: stream.isLoading,
|
||||
isThreadLoading: stream.isThreadLoading,
|
||||
interrupt: stream.interrupt,
|
||||
getMessagesMetadata: stream.getMessagesMetadata,
|
||||
sendMessage,
|
||||
runSingleStep,
|
||||
continueStream,
|
||||
stopStream,
|
||||
markCurrentThreadAsResolved,
|
||||
resumeInterrupt,
|
||||
};
|
||||
}
|
||||
|
||||
+7
-7
@@ -3,7 +3,7 @@
|
||||
import React, { useState, useEffect, useCallback, Suspense } from "react";
|
||||
import Image from "next/image";
|
||||
import { useQueryState } from "nuqs";
|
||||
import { getConfig, saveConfig, StandaloneConfig } from "@/lib/config";
|
||||
import { getConfig, saveConfig, DeploymentConfig } from "@/lib/config";
|
||||
import { ConfigDialog } from "@/app/components/ConfigDialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Assistant } from "@langchain/langgraph-sdk";
|
||||
@@ -19,10 +19,10 @@ import { ChatProvider } from "@/providers/ChatProvider";
|
||||
import { ChatInterface } from "@/app/components/ChatInterface";
|
||||
|
||||
interface HomePageInnerProps {
|
||||
config: StandaloneConfig;
|
||||
config: DeploymentConfig;
|
||||
configDialogOpen: boolean;
|
||||
setConfigDialogOpen: (open: boolean) => void;
|
||||
handleSaveConfig: (config: StandaloneConfig) => void;
|
||||
handleSaveConfig: (config: DeploymentConfig) => void;
|
||||
}
|
||||
|
||||
function HomePageInner({
|
||||
@@ -170,7 +170,7 @@ function HomePageInner({
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<ResizablePanelGroup
|
||||
direction="horizontal"
|
||||
autoSaveId="standalone-chat"
|
||||
autoSaveId="evoscientist-chat"
|
||||
>
|
||||
{sidebar && (
|
||||
<>
|
||||
@@ -214,7 +214,7 @@ function HomePageInner({
|
||||
}
|
||||
|
||||
function HomePageContent() {
|
||||
const [config, setConfig] = useState<StandaloneConfig | null>(null);
|
||||
const [config, setConfig] = useState<DeploymentConfig | null>(null);
|
||||
const [configDialogOpen, setConfigDialogOpen] = useState(false);
|
||||
const [assistantId, setAssistantId] = useQueryState("assistantId");
|
||||
|
||||
@@ -239,7 +239,7 @@ function HomePageContent() {
|
||||
}
|
||||
}, [config, assistantId, setAssistantId]);
|
||||
|
||||
const handleSaveConfig = useCallback((newConfig: StandaloneConfig) => {
|
||||
const handleSaveConfig = useCallback((newConfig: DeploymentConfig) => {
|
||||
saveConfig(newConfig);
|
||||
setConfig(newConfig);
|
||||
}, []);
|
||||
@@ -257,7 +257,7 @@ function HomePageContent() {
|
||||
/>
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-2xl font-bold">Welcome to Standalone Chat</h1>
|
||||
<h1 className="text-2xl font-bold">Welcome to EvoScientist</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Configure your deployment to get started
|
||||
</p>
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import { Message } from "@langchain/langgraph-sdk";
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function extractStringFromMessageContent(message: Message): string {
|
||||
return typeof message.content === "string"
|
||||
@@ -60,98 +54,3 @@ export function extractSubAgentContent(data: unknown): string {
|
||||
// Fallback for any other type
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
export function isPreparingToCallTaskTool(messages: Message[]): boolean {
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
return (
|
||||
(lastMessage.type === "ai" &&
|
||||
lastMessage.tool_calls?.some(
|
||||
(call: { name?: string }) => call.name === "task"
|
||||
)) ||
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
export function formatMessageForLLM(message: Message): string {
|
||||
let role: string;
|
||||
if (message.type === "human") {
|
||||
role = "Human";
|
||||
} else if (message.type === "ai") {
|
||||
role = "Assistant";
|
||||
} else if (message.type === "tool") {
|
||||
role = `Tool Result`;
|
||||
} else {
|
||||
role = message.type || "Unknown";
|
||||
}
|
||||
|
||||
const timestamp = message.id ? ` (${message.id.slice(0, 8)})` : "";
|
||||
|
||||
let contentText = "";
|
||||
|
||||
// Extract content text
|
||||
if (typeof message.content === "string") {
|
||||
contentText = message.content;
|
||||
} else if (Array.isArray(message.content)) {
|
||||
const textParts: string[] = [];
|
||||
|
||||
message.content.forEach((part: any) => {
|
||||
if (typeof part === "string") {
|
||||
textParts.push(part);
|
||||
} else if (part && typeof part === "object" && part.type === "text") {
|
||||
textParts.push(part.text || "");
|
||||
}
|
||||
// Ignore other types like tool_use in content - we handle tool calls separately
|
||||
});
|
||||
|
||||
contentText = textParts.join("\n\n").trim();
|
||||
}
|
||||
|
||||
// For tool messages, include additional tool metadata
|
||||
if (message.type === "tool") {
|
||||
const toolName = (message as any).name || "unknown_tool";
|
||||
const toolCallId = (message as any).tool_call_id || "";
|
||||
role = `Tool Result [${toolName}]`;
|
||||
if (toolCallId) {
|
||||
role += ` (call_id: ${toolCallId.slice(0, 8)})`;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle tool calls from .tool_calls property (for AI messages)
|
||||
const toolCallsText: string[] = [];
|
||||
if (
|
||||
message.type === "ai" &&
|
||||
message.tool_calls &&
|
||||
Array.isArray(message.tool_calls) &&
|
||||
message.tool_calls.length > 0
|
||||
) {
|
||||
message.tool_calls.forEach((call: any) => {
|
||||
const toolName = call.name || "unknown_tool";
|
||||
const toolArgs = call.args ? JSON.stringify(call.args, null, 2) : "{}";
|
||||
toolCallsText.push(`[Tool Call: ${toolName}]\nArguments: ${toolArgs}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Combine content and tool calls
|
||||
const parts: string[] = [];
|
||||
if (contentText) {
|
||||
parts.push(contentText);
|
||||
}
|
||||
if (toolCallsText.length > 0) {
|
||||
parts.push(...toolCallsText);
|
||||
}
|
||||
|
||||
if (parts.length === 0) {
|
||||
return `${role}${timestamp}: [Empty message]`;
|
||||
}
|
||||
|
||||
if (parts.length === 1) {
|
||||
return `${role}${timestamp}: ${parts[0]}`;
|
||||
}
|
||||
|
||||
return `${role}${timestamp}:\n${parts.join("\n\n")}`;
|
||||
}
|
||||
|
||||
export function formatConversationForLLM(messages: Message[]): string {
|
||||
const formattedMessages = messages.map(formatMessageForLLM);
|
||||
return formattedMessages.join("\n\n---\n\n");
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
export interface StandaloneConfig {
|
||||
export interface DeploymentConfig {
|
||||
deploymentUrl: string;
|
||||
assistantId: string;
|
||||
langsmithApiKey?: string;
|
||||
@@ -11,14 +11,14 @@ export const DEFAULT_ASSISTANT_ID = "EvoScientist";
|
||||
|
||||
const CONFIG_KEY = "evoscientist-config";
|
||||
|
||||
export function getConfig(): StandaloneConfig | null {
|
||||
export function getConfig(): DeploymentConfig | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
|
||||
const stored = localStorage.getItem(CONFIG_KEY);
|
||||
if (!stored) return null;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(stored) as StandaloneConfig;
|
||||
const parsed = JSON.parse(stored) as DeploymentConfig;
|
||||
// Always pin the assistant to the EvoScientist main agent.
|
||||
return { ...parsed, assistantId: DEFAULT_ASSISTANT_ID };
|
||||
} catch {
|
||||
@@ -26,7 +26,7 @@ export function getConfig(): StandaloneConfig | null {
|
||||
}
|
||||
}
|
||||
|
||||
export function saveConfig(config: StandaloneConfig): void {
|
||||
export function saveConfig(config: DeploymentConfig): void {
|
||||
if (typeof window === "undefined") return;
|
||||
localStorage.setItem(CONFIG_KEY, JSON.stringify(config));
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { blackA, green, mauve, slate, violet } from "@radix-ui/colors";
|
||||
import plugin from "tailwindcss/plugin";
|
||||
import containerQueries from "@tailwindcss/container-queries";
|
||||
import typography from "@tailwindcss/typography";
|
||||
import forms from "@tailwindcss/forms";
|
||||
import tailwindcssAnimate from "tailwindcss-animate";
|
||||
import headlessui from "@headlessui/tailwindcss";
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
@@ -366,11 +363,9 @@ export default {
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
containerQueries,
|
||||
typography,
|
||||
forms,
|
||||
tailwindcssAnimate,
|
||||
headlessui,
|
||||
plugin(({ addUtilities, addBase }) => {
|
||||
addBase({
|
||||
input: {
|
||||
|
||||
Reference in New Issue
Block a user