samples updating with AgentScope-Runtime 1.0.0 (#43)

This commit is contained in:
Zhiling (Bruce) Luo
2025-12-09 21:19:33 +08:00
committed by GitHub
parent 67469f1caa
commit 68450961bb
10 changed files with 353 additions and 271 deletions

View File

@@ -1,56 +1,41 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import functools
import os import os
import threading
from typing import List, Dict, AsyncGenerator from typing import List, Dict, AsyncGenerator
from openai import OpenAI
from agentscope.agent import ReActAgent from agentscope.agent import ReActAgent
from agentscope.formatter import DashScopeChatFormatter
from agentscope.message import TextBlock
from agentscope.model import DashScopeChatModel from agentscope.model import DashScopeChatModel
from agentscope_runtime.engine import Runner from agentscope.pipeline import stream_printing_messages
from agentscope_runtime.engine.agents.agentscope_agent import AgentScopeAgent from agentscope.tool import Toolkit, ToolResponse
from agentscope_runtime.engine.schemas.agent_schemas import (
AgentRequest,
RunStatus,
)
from agentscope_runtime.engine.services import SandboxService
from agentscope_runtime.engine.services.context_manager import ContextManager
from agentscope_runtime.engine.services.environment_manager import (
EnvironmentManager,
)
from agentscope_runtime.engine.services.memory_service import (
InMemoryMemoryService,
)
from agentscope_runtime.engine.services.session_history_service import (
InMemorySessionHistoryService,
)
from agentscope_runtime.sandbox.tools.browser import (
browser_click,
browser_close,
browser_console_messages,
browser_drag,
browser_file_upload,
browser_handle_dialog,
browser_hover,
browser_navigate,
browser_navigate_back,
browser_navigate_forward,
browser_network_requests,
browser_pdf_save,
browser_press_key,
browser_resize,
browser_select_option,
browser_snapshot,
browser_tab_close,
browser_tab_list,
browser_tab_new,
browser_tab_select,
browser_take_screenshot,
browser_type,
browser_wait_for,
run_ipython_cell,
run_shell_command,
)
from prompts import SYSTEM_PROMPT from prompts import SYSTEM_PROMPT
from agentscope_runtime.adapters.agentscope.memory import (
AgentScopeSessionHistoryMemory,
)
from agentscope_runtime.engine import AgentApp
from agentscope_runtime.engine.schemas.agent_schemas import (
AgentRequest,
)
from agentscope_runtime.engine.services.agent_state.state_service import (
InMemoryStateService,
)
from agentscope_runtime.engine.services.sandbox.sandbox_service import (
SandboxService,
)
# flake8: noqa: E501
from agentscope_runtime.engine.services.session_history.session_history_service import ( # pylint: disable=line-too-long
InMemorySessionHistoryService, # pylint: disable=line-too-long
)
if os.path.exists(".env"): if os.path.exists(".env"):
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -59,116 +44,162 @@ if os.path.exists(".env"):
USER_ID = "user_1" USER_ID = "user_1"
SESSION_ID = "session_001" # Using a fixed ID for simplicity SESSION_ID = "session_001" # Using a fixed ID for simplicity
PORT = 8090
class AgentscopeBrowseruseAgent:
def __init__(self) -> None: def sandbox_tool_adapter(func):
self.tools = [ """
run_shell_command, Sandbox Tool Adapter.
run_ipython_cell, Wraps a sandbox tool function so that its output is always converted
browser_close, into a ToolResponse object, which is required by the Toolkit.
browser_resize, This adapter preserves the original function signature and docstring
browser_console_messages, so that JSON schemas can be correctly generated by the Toolkit.
browser_handle_dialog, Args:
browser_file_upload, func: Original sandbox tool function (may return dict, string, etc.).
browser_press_key, Returns:
browser_navigate, A callable that produces ToolResponse instead of raw data.
browser_navigate_back, """
browser_navigate_forward,
browser_network_requests, @functools.wraps(func)
browser_pdf_save, def wrapper(*args, **kwargs):
browser_take_screenshot, res = func(*args, **kwargs)
browser_snapshot, if isinstance(res, ToolResponse):
browser_click, return res
browser_drag, # TODO: fix this
browser_hover, return ToolResponse(content=[TextBlock(type="text", text=str(res))])
browser_type,
browser_select_option, return wrapper
browser_tab_list,
browser_tab_new,
browser_tab_select, desktop_url = ""
browser_tab_close,
browser_wait_for,
def init():
agent_app = AgentApp(
app_name="Friday",
app_description="A helpful assistant",
)
@agent_app.init
async def init_func(self):
self.state_service = InMemoryStateService()
self.session_service = InMemorySessionHistoryService()
self.sandbox_service = SandboxService()
await self.state_service.start()
await self.session_service.start()
await self.sandbox_service.start()
@agent_app.shutdown
async def shutdown_func(self):
await self.state_service.stop()
await self.session_service.stop()
await self.sandbox_service.stop()
@agent_app.query(framework="agentscope")
async def query_func(
self,
msgs,
request: AgentRequest = None,
):
session_id = request.session_id
user_id = request.user_id
state = await self.state_service.export_state(
session_id=session_id,
user_id=user_id,
)
# Get sandbox
sandboxes = self.sandbox_service.connect(
session_id=session_id,
user_id=user_id,
sandbox_types=["browser"],
)
sandbox = sandboxes[0]
global desktop_url
desktop_url = sandbox.desktop_url
browser_tools = [
sandbox.browser_navigate,
sandbox.browser_take_screenshot,
sandbox.browser_snapshot,
sandbox.browser_click,
sandbox.browser_type,
] ]
self.agent = AgentScopeAgent(
toolkit = Toolkit()
for tool in browser_tools:
toolkit.register_tool_function(sandbox_tool_adapter(tool))
agent = ReActAgent(
name="Friday", name="Friday",
model=DashScopeChatModel( model=DashScopeChatModel(
"qwen-max", "qwen-max",
api_key=os.getenv("DASHSCOPE_API_KEY"), api_key=os.getenv("DASHSCOPE_API_KEY"),
enable_thinking=True,
stream=True,
), ),
agent_config={ sys_prompt=SYSTEM_PROMPT,
"sys_prompt": SYSTEM_PROMPT, toolkit=toolkit,
}, memory=AgentScopeSessionHistoryMemory(
tools=self.tools, service=self.session_service,
agent_builder=ReActAgent, session_id=session_id,
user_id=user_id,
),
formatter=DashScopeChatFormatter(),
) )
async def connect(self) -> None: if state:
session_history_service = InMemorySessionHistoryService() agent.load_state_dict(state)
await session_history_service.create_session( async for msg, last in stream_printing_messages(
user_id=USER_ID, agents=[agent],
session_id=SESSION_ID, coroutine_task=agent(msgs),
):
yield msg, last
state = agent.state_dict()
await self.state_service.save_state(
user_id=user_id,
session_id=session_id,
state=state,
) )
self.mem_service = InMemoryMemoryService() def run_agent_app():
await self.mem_service.start() agent_app.run(host="127.0.0.1", port=PORT)
self.sandbox_service = SandboxService()
await self.sandbox_service.start()
self.context_manager = ContextManager( threading.Thread(target=run_agent_app, daemon=True).start()
memory_service=self.mem_service, return agent_app
session_history_service=session_history_service,
)
self.environment_manager = EnvironmentManager(
sandbox_service=self.sandbox_service,
)
sandboxes = self.sandbox_service.connect(
session_id=SESSION_ID,
user_id=USER_ID,
tools=self.tools,
)
if len(sandboxes) > 0:
sandbox = sandboxes[0]
self.desktop_url = sandbox.desktop_url
else:
self.desktop_url = ""
runner = Runner( class AgentscopeBrowseruseAgent:
agent=self.agent, def __init__(self) -> None:
context_manager=self.context_manager, self.agent = init()
environment_manager=self.environment_manager, self.desktop_url = ""
)
self.runner = runner
async def chat( async def chat(
self, self,
chat_messages: List[Dict], chat_messages: List[Dict],
) -> AsyncGenerator[Dict, None]: ) -> AsyncGenerator[Dict, None]:
convert_messages = [] client = OpenAI(base_url=f"http://127.0.0.1:{PORT}/compatible-mode/v1")
for chat_message in chat_messages:
convert_messages.append( stream = client.responses.create(
{ model="any_name",
"role": chat_message["role"], input=chat_messages[-1]["content"],
"content": [ stream=True,
{ )
"type": "text", global desktop_url
"text": chat_message["content"],
}, self.desktop_url = desktop_url
], for chunk in stream:
}, if hasattr(chunk, "delta"):
) yield chunk.delta
request = AgentRequest(input=convert_messages, session_id=SESSION_ID) else:
request.tools = [] yield {}
async for message in self.runner.stream_query( # if chunk.choices[0].delta.content is not None:
user_id=USER_ID, # yield chunk.choices[0].delta.content
request=request,
):
if (
message.object == "message"
and RunStatus.Completed == message.status
):
yield message.content
async def close(self) -> None: async def close(self) -> None:
await self.sandbox_service.stop() await self.sandbox_service.stop()

View File

@@ -1,17 +1,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import asyncio
import json import json
import logging import logging
import os import os
import time import time
from quart import Quart, Response, jsonify, request
from quart_cors import cors
from agentscope_browseruse_agent import AgentscopeBrowseruseAgent from agentscope_browseruse_agent import AgentscopeBrowseruseAgent
from agentscope_runtime.engine.schemas.agent_schemas import ( from agentscope_runtime.engine.schemas.agent_schemas import (
DataContent, DataContent,
TextContent, TextContent,
) )
from quart import Quart, Response, jsonify, request
from quart_cors import cors
app = Quart(__name__) app = Quart(__name__)
app = cors(app, allow_origin="*") app = cors(app, allow_origin="*")
@@ -35,9 +36,8 @@ if os.path.exists(".env"):
async def user_mode(input_data): async def user_mode(input_data):
messages = input_data.get("messages", []) messages = input_data.get("messages", [])
last_name = "" last_name = ""
async for item_list in agent.chat(messages): async for item in agent.chat(messages):
if item_list: if item:
item = item_list[0]
res = "" res = ""
if isinstance(item, TextContent): if isinstance(item, TextContent):
res = item.text res = item.text
@@ -48,8 +48,9 @@ async def user_mode(input_data):
continue continue
res = "I will use the tool" + json.dumps(item.data["name"]) res = "I will use the tool" + json.dumps(item.data["name"])
last_name = json.dumps(item.data["name"]) last_name = json.dumps(item.data["name"])
elif isinstance(item, str):
yield simple_yield(res + "\n") res = item
yield simple_yield(res)
else: else:
yield simple_yield() yield simple_yield()
@@ -105,5 +106,5 @@ async def get_env_info():
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(agent.connect()) agent.chat([{"role": "user", "content": "hello"}])
app.run(host="0.0.0.0", port=9000) app.run(host="0.0.0.0", port=9000)

View File

@@ -1,5 +1,6 @@
pyyaml>=6.0.2 pyyaml>=6.0.2
quart>=0.8.0 quart>=0.8.0
quart-cors>=0.8.0 quart-cors>=0.8.0
agentscope-runtime==0.2.0 agentscope-runtime>=1.0.0
agentscope[full]>=1.0.5 agentscope[full]>=1.0.5
openai>=2.8.1

View File

@@ -94,16 +94,19 @@ const App: React.FC = () => {
setMessages(newMessages); setMessages(newMessages);
setIsTyping(true); setIsTyping(true);
await processMessageToChatGPT(newMessages); await processMessageToChatGPT(newMessages);
await get_desktop_url();
}; };
async function processMessageToChatGPT(chatMessages: ChatMessage) { async function processMessageToChatGPT(chatMessages: ChatMessage) {
let apiMessages = chatMessages const apiMessages = chatMessages
.map((messageObject) => { .map((messageObject) => {
if (messageObject.message.trim() === "") { if (messageObject.message.trim() === "") {
return null; return null;
} }
let role = messageObject.sender === "assistant" ? "assistant" : "user"; const role =
messageObject.sender === "assistant" ? "assistant" : "user";
return { role, content: messageObject.message }; return { role, content: messageObject.message };
}) })
.filter(Boolean); .filter(Boolean);
@@ -143,7 +146,9 @@ const App: React.FC = () => {
]); ]);
while (true) { while (true) {
const { done, value } = await reader.read(); const { done, value } = await reader.read();
if (done) break; if (done) {
break;
}
const chunk = decoder.decode(value); const chunk = decoder.decode(value);
accumulatedMessage += chunk; accumulatedMessage += chunk;
@@ -152,7 +157,9 @@ const App: React.FC = () => {
accumulatedMessage = lines.pop() || ""; accumulatedMessage = lines.pop() || "";
for (const line of lines) { for (const line of lines) {
if (line.trim() === "") continue; if (line.trim() === "") {
continue;
}
try { try {
const parsed = JSON.parse(line.split("data: ")[1]); const parsed = JSON.parse(line.split("data: ")[1]);

View File

@@ -1,6 +1,5 @@
DASHSCOPE_API_KEY= DASHSCOPE_API_KEY=
DASHSCOPE_BASE_URL= DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
SERVER_PORT=8080 SERVER_PORT=8080
SERVER_ENDPOINT=agent
SERVER_HOST=localhost SERVER_HOST=localhost
USER_MANAGER_STORAGE=user.json USER_MANAGER_STORAGE=user.json

View File

@@ -1,70 +1,109 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import asyncio # pylint:disable=redefined-outer-name, unused-argument
import os import os
from agentscope.formatter import DashScopeChatFormatter
from agentscope.tool import Toolkit, execute_python_code
from agentscope.pipeline import stream_printing_messages
from agentscope.agent import ReActAgent from agentscope.agent import ReActAgent
from agentscope.model import DashScopeChatModel from agentscope.model import DashScopeChatModel
from agentscope_runtime.engine import LocalDeployManager, Runner
from agentscope_runtime.engine.agents.agentscope_agent import AgentScopeAgent from agentscope_runtime.engine import AgentApp
from agentscope_runtime.engine.services.context_manager import ContextManager from agentscope_runtime.engine.schemas.agent_schemas import AgentRequest
from agentscope_runtime.adapters.agentscope.memory import (
AgentScopeSessionHistoryMemory,
)
from agentscope_runtime.engine.services.agent_state import (
InMemoryStateService,
)
from agentscope_runtime.engine.services.session_history import (
InMemorySessionHistoryService,
)
def local_deploy(): def local_deploy():
asyncio.run(_local_deploy())
async def _local_deploy():
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv() load_dotenv()
server_port = int(os.environ.get("SERVER_PORT", "8090")) server_port = int(os.environ.get("SERVER_PORT", "8090"))
server_endpoint = os.environ.get("SERVER_ENDPOINT", "agent") # server_endpoint = os.environ.get("SERVER_ENDPOINT", "process")
model = DashScopeChatModel(
model_name="qwen-turbo", agent_app = AgentApp(
api_key=os.getenv("DASHSCOPE_API_KEY"), app_name="Friday",
) app_description="A simple LLM agent to generate a short response",
agent = AgentScopeAgent(
name="Friday",
model=model,
agent_config={
"sys_prompt": "A simple LLM agent to generate a short response",
},
agent_builder=ReActAgent,
) )
context_manager = ContextManager() @agent_app.init
async def init_func(self):
self.state_service = InMemoryStateService()
self.session_service = InMemorySessionHistoryService()
runner = Runner( await self.state_service.start()
agent=agent, await self.session_service.start()
context_manager=context_manager,
)
deploy_manager = LocalDeployManager(host="localhost", port=server_port) @agent_app.shutdown
try: async def shutdown_func(self):
deployment_info = await runner.deploy( await self.state_service.stop()
deploy_manager, await self.session_service.stop()
endpoint_path=f"/{server_endpoint}",
@agent_app.query(framework="agentscope")
async def query_func(
self,
msgs,
request: AgentRequest = None,
**kwargs,
):
session_id = request.session_id
user_id = request.user_id
state = await self.state_service.export_state(
session_id=session_id,
user_id=user_id,
) )
print("✅ Service deployed successfully!") toolkit = Toolkit()
print(f" URL: {deployment_info['url']}") toolkit.register_tool_function(execute_python_code)
print(f" Endpoint: {deployment_info['url']}/{server_endpoint}")
print("\nAgent Service is running in the background.")
while True: agent = ReActAgent(
await asyncio.sleep(1) name="Friday",
model=DashScopeChatModel(
"qwen-turbo",
api_key=os.getenv("DASHSCOPE_API_KEY"),
enable_thinking=True,
stream=True,
),
sys_prompt="You're a helpful assistant named Friday.",
toolkit=toolkit,
memory=AgentScopeSessionHistoryMemory(
service=self.session_service,
session_id=session_id,
user_id=user_id,
),
formatter=DashScopeChatFormatter(),
)
except (KeyboardInterrupt, asyncio.CancelledError): if state:
# This block will be executed when you press Ctrl+C. agent.load_state_dict(state)
print("\nShutdown signal received. Stopping the service...")
if deploy_manager.is_running: async for msg, last in stream_printing_messages(
await deploy_manager.stop() agents=[agent],
print("✅ Service stopped.") coroutine_task=agent(msgs),
except Exception as e: ):
print(f"An error occurred: {e}") yield msg, last
if deploy_manager.is_running:
await deploy_manager.stop() state = agent.state_dict()
await self.state_service.save_state(
user_id=user_id,
session_id=session_id,
state=state,
)
agent_app.run(host="127.0.0.1", port=server_port)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -1,5 +1,6 @@
flask>=3.1.2 flask>=3.1.2
flask_cors>=6.0.1 flask_cors>=6.0.1
agentscope-runtime==0.2.0 agentscope-runtime>=1.0.0
agentscope-runtime[agentscope] agentscope-runtime[agentscope]
flask_sqlalchemy>=3.1.1 flask_sqlalchemy>=3.1.1
openai>=2.8.1

View File

@@ -5,6 +5,8 @@ import os
from datetime import datetime from datetime import datetime
import requests import requests
from openai import OpenAI
from dotenv import load_dotenv from dotenv import load_dotenv
from flask import Flask, request, jsonify from flask import Flask, request, jsonify
from flask_cors import CORS from flask_cors import CORS
@@ -152,29 +154,25 @@ def sse_client(url, data=None):
pass pass
def call_runner(query, query_user_id, query_session_id): def call_runner(query):
server_port = int(os.environ.get("SERVER_PORT", "8090")) server_port = int(os.environ.get("SERVER_PORT", "8090"))
server_endpoint = os.environ.get("SERVER_ENDPOINT", "agent")
server_host = os.environ.get("SERVER_HOST", "localhost") server_host = os.environ.get("SERVER_HOST", "localhost")
url = f"http://{server_host}:{server_port}/{server_endpoint}" client = OpenAI(
data_arg = { base_url=f"http://{server_host}:{server_port}/compatible-mode/v1",
"input": [ )
{
"role": "user", stream = client.responses.create(
"content": [ model="any_name",
{ input=query,
"type": "text", stream=True,
"text": query, )
},
], for chunk in stream:
}, if hasattr(chunk, "delta"):
], yield chunk.delta
"session_id": query_session_id, else:
"user_id": query_user_id, yield ""
}
for content in sse_client(url, data=data_arg):
yield content
# API routes # API routes
@@ -359,11 +357,9 @@ def send_message(conversation_id):
ai_response_text = "" ai_response_text = ""
question = text question = text
conversation_id_str = str(conversation_id)
for item in call_runner( for item in call_runner(
question, question,
conversation_id_str,
conversation_id_str,
): ):
ai_response_text += item ai_response_text += item

View File

@@ -1,24 +1,24 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from "react";
import { MessageCircle, User, Send, Plus, LogOut, Menu, X, Bot } from 'lucide-react'; import { MessageCircle, User, Send, Plus, LogOut, Menu, X, Bot } from "lucide-react";
const App = () => { const App = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false); const [isLoggedIn, setIsLoggedIn] = useState(false);
const [username, setUsername] = useState(''); const [username, setUsername] = useState("");
const [password, setPassword] = useState(''); const [password, setPassword] = useState("");
const [currentUser, setCurrentUser] = useState(null); const [currentUser, setCurrentUser] = useState(null);
const [conversations, setConversations] = useState([]); const [conversations, setConversations] = useState([]);
const [activeConversation, setActiveConversation] = useState(null); const [activeConversation, setActiveConversation] = useState(null);
const [message, setMessage] = useState(''); const [message, setMessage] = useState("");
const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const messagesEndRef = useRef(null); const messagesEndRef = useRef(null);
// API base URL // API base URL
const API_BASE = 'http://localhost:5100/api'; const API_BASE = "http://localhost:5100/api";
// Auto scroll to bottom of messages // Auto scroll to bottom of messages
useEffect(() => { useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [activeConversation?.messages]); }, [activeConversation?.messages]);
// Fetch user conversations // Fetch user conversations
@@ -34,7 +34,7 @@ const App = () => {
} }
} }
} catch (error) { } catch (error) {
console.error('Error fetching conversations:', error); console.error("Error fetching conversations:", error);
} }
}; };
@@ -47,7 +47,7 @@ const App = () => {
setActiveConversation(data); setActiveConversation(data);
} }
} catch (error) { } catch (error) {
console.error('Error loading conversation:', error); console.error("Error loading conversation:", error);
} }
}; };
@@ -58,9 +58,9 @@ const App = () => {
try { try {
const response = await fetch(`${API_BASE}/login`, { const response = await fetch(`${API_BASE}/login`, {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ username, password }), body: JSON.stringify({ username, password }),
}); });
@@ -72,11 +72,11 @@ const App = () => {
await fetchConversations(userData.id); await fetchConversations(userData.id);
} else { } else {
const errorData = await response.json(); const errorData = await response.json();
alert(errorData.error || 'Login failed'); alert(errorData.error || "Login failed");
} }
} catch (error) { } catch (error) {
console.error('Login error:', error); console.error("Login error:", error);
alert('Network error. Please try again.'); alert("Network error. Please try again.");
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -86,8 +86,8 @@ const App = () => {
const handleLogout = () => { const handleLogout = () => {
setIsLoggedIn(false); setIsLoggedIn(false);
setCurrentUser(null); setCurrentUser(null);
setUsername(''); setUsername("");
setPassword(''); setPassword("");
setConversations([]); setConversations([]);
setActiveConversation(null); setActiveConversation(null);
setIsMenuOpen(false); setIsMenuOpen(false);
@@ -95,16 +95,18 @@ const App = () => {
// Create new conversation // Create new conversation
const createNewConversation = async () => { const createNewConversation = async () => {
if (!currentUser) return; if (!currentUser) {
return;
}
setLoading(true); setLoading(true);
try { try {
const response = await fetch(`${API_BASE}/users/${currentUser.id}/conversations`, { const response = await fetch(`${API_BASE}/users/${currentUser.id}/conversations`, {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ title: 'New Conversation' }), body: JSON.stringify({ title: "New Conversation" }),
}); });
if (response.ok) { if (response.ok) {
@@ -113,7 +115,7 @@ const App = () => {
await loadConversation(newConversation.id); await loadConversation(newConversation.id);
} }
} catch (error) { } catch (error) {
console.error('Error creating conversation:', error); console.error("Error creating conversation:", error);
} finally { } finally {
setLoading(false); setLoading(false);
setIsMenuOpen(false); setIsMenuOpen(false);
@@ -122,17 +124,19 @@ const App = () => {
// Send message // Send message
const sendMessage = async () => { const sendMessage = async () => {
if (!message.trim() || !activeConversation) return; if (!message.trim() || !activeConversation) {
return;
}
setLoading(true); setLoading(true);
try { try {
// Send user message // Send user message
const userMessageResponse = await fetch(`${API_BASE}/conversations/${activeConversation.id}/messages`, { const userMessageResponse = await fetch(`${API_BASE}/conversations/${activeConversation.id}/messages`, {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ text: message, sender: 'user' }), body: JSON.stringify({ text: message, sender: "user" }),
}); });
if (userMessageResponse.ok) { if (userMessageResponse.ok) {
@@ -142,16 +146,16 @@ const App = () => {
const updatedConversation = { const updatedConversation = {
...activeConversation, ...activeConversation,
messages: [...activeConversation.messages, userMessage], messages: [...activeConversation.messages, userMessage],
title: activeConversation.messages.length === 1 ? message.slice(0, 20) + (message.length > 20 ? '...' : '') : activeConversation.title title: activeConversation.messages.length === 1 ? message.slice(0, 20) + (message.length > 20 ? "..." : "") : activeConversation.title
}; };
setActiveConversation(updatedConversation); setActiveConversation(updatedConversation);
setMessage(''); setMessage("");
// Fetch updated conversation to get AI response // Fetch updated conversation to get AI response
await loadConversation(activeConversation.id); await loadConversation(activeConversation.id);
} }
} catch (error) { } catch (error) {
console.error('Error sending message:', error); console.error("Error sending message:", error);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -159,9 +163,9 @@ const App = () => {
// Format timestamp // Format timestamp
const formatTime = (timestamp) => { const formatTime = (timestamp) => {
return new Date(timestamp).toLocaleTimeString('en-US', { return new Date(timestamp).toLocaleTimeString("en-US", {
hour: '2-digit', hour: "2-digit",
minute: '2-digit' minute: "2-digit"
}); });
}; };
@@ -206,7 +210,7 @@ const App = () => {
disabled={loading} disabled={loading}
className="w-full bg-indigo-600 text-white py-3 rounded-lg font-medium hover:bg-indigo-700 transition-colors focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-50" className="w-full bg-indigo-600 text-white py-3 rounded-lg font-medium hover:bg-indigo-700 transition-colors focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-50"
> >
{loading ? 'Logging in...' : 'Login'} {loading ? "Logging in..." : "Login"}
</button> </button>
</form> </form>
@@ -283,7 +287,7 @@ const App = () => {
setIsMenuOpen(false); setIsMenuOpen(false);
}} }}
className={`p-4 border-b border-gray-100 cursor-pointer hover:bg-gray-50 transition-colors ${ className={`p-4 border-b border-gray-100 cursor-pointer hover:bg-gray-50 transition-colors ${
activeConversation?.id === conversation.id ? 'bg-indigo-50 border-l-4 border-l-indigo-500' : '' activeConversation?.id === conversation.id ? "bg-indigo-50 border-l-4 border-l-indigo-500" : ""
}`} }`}
> >
<div className="flex items-start space-x-3"> <div className="flex items-start space-x-3">
@@ -291,7 +295,7 @@ const App = () => {
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<h3 className="font-medium text-gray-900 truncate">{conversation.title}</h3> <h3 className="font-medium text-gray-900 truncate">{conversation.title}</h3>
<p className="text-sm text-gray-500 truncate"> <p className="text-sm text-gray-500 truncate">
{conversation.preview || 'New conversation'} {conversation.preview || "New conversation"}
</p> </p>
</div> </div>
</div> </div>
@@ -328,17 +332,17 @@ const App = () => {
{activeConversation.messages.map((msg) => ( {activeConversation.messages.map((msg) => (
<div <div
key={msg.id} key={msg.id}
className={`flex ${msg.sender === 'user' ? 'justify-end' : 'justify-start'}`} className={`flex ${msg.sender === "user" ? "justify-end" : "justify-start"}`}
> >
<div <div
className={`max-w-xs lg:max-w-md px-4 py-3 rounded-2xl ${ className={`max-w-xs lg:max-w-md px-4 py-3 rounded-2xl ${
msg.sender === 'user' msg.sender === "user"
? 'bg-indigo-600 text-white rounded-br-md' ? "bg-indigo-600 text-white rounded-br-md"
: 'bg-white text-gray-800 border border-gray-200 rounded-bl-md shadow-sm' : "bg-white text-gray-800 border border-gray-200 rounded-bl-md shadow-sm"
}`} }`}
> >
<p className="text-sm">{msg.text}</p> <p className="text-sm">{msg.text}</p>
<p className={`text-xs mt-1 ${msg.sender === 'user' ? 'text-indigo-100' : 'text-gray-500'}`}> <p className={`text-xs mt-1 ${msg.sender === "user" ? "text-indigo-100" : "text-gray-500"}`}>
{formatTime(msg.created_at)} {formatTime(msg.created_at)}
</p> </p>
</div> </div>
@@ -354,7 +358,7 @@ const App = () => {
type="text" type="text"
value={message} value={message}
onChange={(e) => setMessage(e.target.value)} onChange={(e) => setMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && !loading && sendMessage()} onKeyPress={(e) => e.key === "Enter" && !loading && sendMessage()}
disabled={loading} disabled={loading}
className="flex-1 px-4 py-3 border border-gray-300 rounded-full focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all disabled:opacity-50" className="flex-1 px-4 py-3 border border-gray-300 rounded-full focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all disabled:opacity-50"
placeholder="Type a message..." placeholder="Type a message..."

View File

@@ -12,10 +12,14 @@ from langchain_core.runnables import RunnableConfig
from langgraph.graph import START, END from langgraph.graph import START, END
from langgraph.graph import StateGraph from langgraph.graph import StateGraph
from langgraph.types import Send from langgraph.types import Send
from agentscope_runtime.engine.agents.langgraph_agent import LangGraphAgent
from agentscope_runtime.engine.helpers.helper import simple_call_agent_direct
from configuration import Configuration from state import (
OverallState,
QueryGenerationState,
ReflectionState,
WebSearchState,
)
from llm_utils import call_dashscope, extract_json_from_qwen
from custom_search_tool import CustomSearchTool from custom_search_tool import CustomSearchTool
from llm_prompts import ( from llm_prompts import (
query_writer_instructions, query_writer_instructions,
@@ -23,13 +27,12 @@ from llm_prompts import (
reflection_instructions, reflection_instructions,
answer_instructions, answer_instructions,
) )
from llm_utils import call_dashscope, extract_json_from_qwen from configuration import Configuration
from state import (
OverallState, from agentscope_runtime.engine.agents.langgraph_agent import LangGraphAgent
QueryGenerationState, from agentscope_runtime.engine.helpers.helper import simple_call_agent_direct
ReflectionState,
WebSearchState,
)
from .utils import ( from .utils import (
get_research_topic, get_research_topic,
insert_citation_markers, insert_citation_markers,