samples updating with AgentScope-Runtime 1.0.0 (#43)
This commit is contained in:
committed by
GitHub
parent
67469f1caa
commit
68450961bb
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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__":
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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..."
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user