diff --git a/py/noxfile.py b/py/noxfile.py index 6ccb346d..39735537 100644 --- a/py/noxfile.py +++ b/py/noxfile.py @@ -378,6 +378,8 @@ def test_strands(session, version): @nox.session() @nox.parametrize("version", AGENTSCOPE_VERSIONS, ids=AGENTSCOPE_VERSIONS) def test_agentscope(session, version): + if version == LATEST and sys.version_info < (3, 11): + session.skip("AgentScope 2.x requires Python 3.11+") _install_test_deps(session) _install_matrix_dep(session, "agentscope", version) _install_group_locked(session, "test-agentscope") diff --git a/py/pyproject.toml b/py/pyproject.toml index e0e8827c..ddf7bcfe 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -351,7 +351,7 @@ latest = "agno==2.6.9" "2.1.0" = "agno==2.1.0" [tool.braintrust.matrix.agentscope] -latest = "agentscope==1.0.21" +latest = "agentscope==2.0.0" "1.0.0" = "agentscope==1.0.0" [tool.braintrust.matrix.autogen-agentchat] diff --git a/py/src/braintrust/integrations/agentscope/cassettes/latest/test_auto_agentscope.yaml b/py/src/braintrust/integrations/agentscope/cassettes/latest/test_auto_agentscope.yaml index 6f7afd0c..d70b07e8 100644 --- a/py/src/braintrust/integrations/agentscope/cassettes/latest/test_auto_agentscope.yaml +++ b/py/src/braintrust/integrations/agentscope/cassettes/latest/test_auto_agentscope.yaml @@ -1,8 +1,8 @@ interactions: - request: body: '{"messages":[{"role":"system","name":"system","content":[{"type":"text","text":"You - are a helpful assistant. Be brief."}]},{"role":"user","name":"user","content":[{"type":"text","text":"Say - hi in two words."}]}],"model":"gpt-4o-mini","stream":true,"stream_options":{"include_usage":true},"temperature":0}' + are a concise assistant. Answer in one sentence."}]},{"role":"user","name":"user","content":[{"type":"text","text":"Say + hello in exactly two words."}]}],"model":"gpt-4o-mini","stream":false,"temperature":0}' headers: Accept: - application/json @@ -11,7 +11,7 @@ interactions: Connection: - keep-alive Content-Length: - - '304' + - '290' Content-Type: - application/json Host: @@ -40,40 +40,136 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-DX7NZBTmBc9FvHicZnqomTwNlJ2xI","object":"chat.completion.chunk","created":1776785561,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_a7190374f3","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"MSWgZo9n2"} - - - data: {"id":"chatcmpl-DX7NZBTmBc9FvHicZnqomTwNlJ2xI","object":"chat.completion.chunk","created":1776785561,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_a7190374f3","choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"EYLOkE"} - - - data: {"id":"chatcmpl-DX7NZBTmBc9FvHicZnqomTwNlJ2xI","object":"chat.completion.chunk","created":1776785561,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_a7190374f3","choices":[{"index":0,"delta":{"content":" - there"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"OsMQm"} - - - data: {"id":"chatcmpl-DX7NZBTmBc9FvHicZnqomTwNlJ2xI","object":"chat.completion.chunk","created":1776785561,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_a7190374f3","choices":[{"index":0,"delta":{"content":"!"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"LJrnGeP6qP"} - - - data: {"id":"chatcmpl-DX7NZBTmBc9FvHicZnqomTwNlJ2xI","object":"chat.completion.chunk","created":1776785561,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_a7190374f3","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"0QFrc"} - - - data: {"id":"chatcmpl-DX7NZBTmBc9FvHicZnqomTwNlJ2xI","object":"chat.completion.chunk","created":1776785561,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_a7190374f3","choices":[],"usage":{"prompt_tokens":29,"completion_tokens":3,"total_tokens":32,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"3tj5TjOjSkh"} - - - data: [DONE] - - - ' + string: "{\n \"id\": \"chatcmpl-DX7NRqUSH6XKNRubUSWlhKnNmPNDB\",\n \"object\": + \"chat.completion\",\n \"created\": 1776785553,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": \"Hello there!\",\n \"refusal\": + null,\n \"annotations\": []\n },\n \"logprobs\": null,\n + \ \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": + 32,\n \"completion_tokens\": 3,\n \"total_tokens\": 35,\n \"prompt_tokens_details\": + {\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_6042092f77\"\n}\n" headers: CF-Cache-Status: - DYNAMIC CF-Ray: - - 9efd715d39a53a03-YYZ + - 9efd712b8cb878a5-YYZ Connection: - keep-alive Content-Type: - - text/event-stream; charset=utf-8 + - application/json + Date: + - Tue, 21 Apr 2026 15:32:33 GMT + Server: + - cloudflare + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + content-length: + - '818' + openai-organization: + - braintrust-data + openai-processing-ms: + - '303' + openai-project: + - proj_vsCSXafhhByzWOThMrJcZiw9 + openai-version: + - '2020-10-01' + set-cookie: + - __cf_bm=vWZsqZxXqzK8jjN5seiu2E8BqryLRFNDYMLsGe_TiUQ-1776785553.2100453-1.0.1.1-jpJ.lEurU5WQQfPpsQ0nN6um.4nJttOu7.z79GA17wHU4ttEm4thC0lQws5XBcsmd95Y8H1r04sdMK4BboF_gKPQUBii3cJy1DzHoD4mGyzEG6boSWTr4toKmbv1ynts; + HttpOnly; Secure; Path=/; Domain=api.openai.com; Expires=Tue, 21 Apr 2026 + 16:02:33 GMT + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999975' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_249eb93d6eb44d4788eac0bc583d79de + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"system","name":"system","content":[{"type":"text","text":"You + are a concise assistant. Answer in one sentence."}]},{"role":"user","name":"user","content":[{"type":"text","text":"Say + hello in exactly two words."}]}],"model":"gpt-4o-mini","stream":false,"temperature":0}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '290' + Content-Type: + - application/json + Host: + - api.openai.com + User-Agent: + - AsyncOpenAI/Python 2.31.0 + X-Stainless-Arch: + - arm64 + X-Stainless-Async: + - async:asyncio + X-Stainless-Lang: + - python + X-Stainless-OS: + - MacOS + X-Stainless-Package-Version: + - 2.31.0 + X-Stainless-Runtime: + - CPython + X-Stainless-Runtime-Version: + - 3.14.3 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-DX7O3zfeUJOH15DVkUmgReCpkVZxE\",\n \"object\": + \"chat.completion\",\n \"created\": 1776785591,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": \"Hello there.\",\n \"refusal\": + null,\n \"annotations\": []\n },\n \"logprobs\": null,\n + \ \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": + 32,\n \"completion_tokens\": 3,\n \"total_tokens\": 35,\n \"prompt_tokens_details\": + {\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_6042092f77\"\n}\n" + headers: + CF-Cache-Status: + - DYNAMIC + CF-Ray: + - 9efd721b1877964a-YYZ + Connection: + - keep-alive + Content-Type: + - application/json Date: - - Tue, 21 Apr 2026 15:32:41 GMT + - Tue, 21 Apr 2026 15:33:11 GMT Server: - cloudflare Strict-Transport-Security: @@ -86,18 +182,20 @@ interactions: - X-Request-ID alt-svc: - h3=":443"; ma=86400 + content-length: + - '818' openai-organization: - braintrust-data openai-processing-ms: - - '154' + - '364' openai-project: - proj_vsCSXafhhByzWOThMrJcZiw9 openai-version: - '2020-10-01' set-cookie: - - __cf_bm=PZv8LQidJAxjTDU4p9DA3ZPYHiXPGPyY_yhofay0BKY-1776785561.160069-1.0.1.1-GtP3HMxTLMRVoqZ.qRVYzRiP9D5xXLeNSfYc2L1uh17sfJU_JDi2C9GtNL8_WuOaaAuEjRvjpPzcLBZcbrY7zDtljrRC0NkG.74n4YLisUDTBu06g9VJdZ6OyV4Pm.kl; + - __cf_bm=0gBwjzu6S8NGJOZz8DHRfVPsaoNL7ANqedT8QLla.yg-1776785591.536904-1.0.1.1-8DZ8d3JNr0lAQVcDW_i1puCgA6zGjTDBjyq.9ZvoVuNK_ag_QIUapir9HQ0pbs6dOeD.7gMY9LWxtPR_K3mVM4jsFdH5p9pye0F0AXwbEYueTCwD4n9ncXY9D2TH4daa; HttpOnly; Secure; Path=/; Domain=api.openai.com; Expires=Tue, 21 Apr 2026 - 16:02:41 GMT + 16:03:11 GMT x-openai-proxy-wasm: - v0.1 x-ratelimit-limit-requests: @@ -107,13 +205,13 @@ interactions: x-ratelimit-remaining-requests: - '29999' x-ratelimit-remaining-tokens: - - '149999980' + - '149999975' x-ratelimit-reset-requests: - 2ms x-ratelimit-reset-tokens: - 0s x-request-id: - - req_647b1639b38341d79da968edc2bff915 + - req_1e92a22c0c334c628bcaf2941ab2d3ba status: code: 200 message: OK diff --git a/py/src/braintrust/integrations/agentscope/integration.py b/py/src/braintrust/integrations/agentscope/integration.py index fac8fe8d..48d07c27 100644 --- a/py/src/braintrust/integrations/agentscope/integration.py +++ b/py/src/braintrust/integrations/agentscope/integration.py @@ -4,10 +4,12 @@ from .patchers import ( AgentCallPatcher, + AgentReplyPatcher, ChatModelPatcher, FanoutPipelinePatcher, SequentialPipelinePatcher, ToolkitCallToolFunctionPatcher, + ToolkitCallToolPatcher, ) @@ -19,8 +21,10 @@ class AgentScopeIntegration(BaseIntegration): min_version = "1.0.0" patchers = ( AgentCallPatcher, + AgentReplyPatcher, SequentialPipelinePatcher, FanoutPipelinePatcher, ToolkitCallToolFunctionPatcher, + ToolkitCallToolPatcher, ChatModelPatcher, ) diff --git a/py/src/braintrust/integrations/agentscope/patchers.py b/py/src/braintrust/integrations/agentscope/patchers.py index a0a9ba21..9a2b8188 100644 --- a/py/src/braintrust/integrations/agentscope/patchers.py +++ b/py/src/braintrust/integrations/agentscope/patchers.py @@ -12,7 +12,7 @@ class AgentCallPatcher(FunctionWrapperPatcher): - """Patch AgentScope agent execution.""" + """Patch AgentScope 1.x agent execution.""" name = "agentscope.agent.call" target_module = "agentscope.agent" @@ -20,6 +20,15 @@ class AgentCallPatcher(FunctionWrapperPatcher): wrapper = _agent_call_wrapper +class AgentReplyPatcher(FunctionWrapperPatcher): + """Patch AgentScope 2.x agent execution.""" + + name = "agentscope.agent.reply" + target_module = "agentscope.agent" + target_path = "Agent.reply" + wrapper = _agent_call_wrapper + + class SequentialPipelinePatcher(FunctionWrapperPatcher): """Patch AgentScope sequential pipeline execution.""" @@ -39,7 +48,7 @@ class FanoutPipelinePatcher(FunctionWrapperPatcher): class ToolkitCallToolFunctionPatcher(FunctionWrapperPatcher): - """Patch AgentScope toolkit execution.""" + """Patch AgentScope 1.x toolkit execution.""" name = "agentscope.tool.call_tool_function" target_module = "agentscope.tool" @@ -47,6 +56,15 @@ class ToolkitCallToolFunctionPatcher(FunctionWrapperPatcher): wrapper = _toolkit_call_tool_function_wrapper +class ToolkitCallToolPatcher(FunctionWrapperPatcher): + """Patch AgentScope 2.x toolkit execution.""" + + name = "agentscope.tool.call_tool" + target_module = "agentscope.tool" + target_path = "Toolkit.call_tool" + wrapper = _toolkit_call_tool_function_wrapper + + class _OpenAIChatModelPatcher(FunctionWrapperPatcher): name = "agentscope.model.openai" target_module = "agentscope.model" diff --git a/py/src/braintrust/integrations/agentscope/test_agentscope.py b/py/src/braintrust/integrations/agentscope/test_agentscope.py index c2154f32..455a9ab4 100644 --- a/py/src/braintrust/integrations/agentscope/test_agentscope.py +++ b/py/src/braintrust/integrations/agentscope/test_agentscope.py @@ -1,9 +1,14 @@ +# pylint: disable=import-error,no-name-in-module,no-value-for-parameter,unexpected-keyword-arg,no-member +import importlib +import importlib.metadata + import pytest from braintrust import logger from braintrust.integrations.agentscope import setup_agentscope from braintrust.integrations.test_utils import verify_autoinstrument_script from braintrust.span_types import SpanTypeAttribute from braintrust.test_helpers import init_test_logger +from packaging.version import Version PROJECT_NAME = "test_agentscope" @@ -11,6 +16,14 @@ setup_agentscope(project_name=PROJECT_NAME) +AGENTSCOPE_VERSION = Version(importlib.metadata.version("agentscope")) +IS_AGENTSCOPE_V2 = AGENTSCOPE_VERSION >= Version("2") +agent_module = importlib.import_module("agentscope.agent") +message_module = importlib.import_module("agentscope.message") +HAS_AGENT_REPLY_API = hasattr(agent_module, "Agent") +HAS_USER_MSG = hasattr(message_module, "UserMsg") + + @pytest.fixture def memory_logger(): init_test_logger(PROJECT_NAME) @@ -26,6 +39,17 @@ def _span_type(span): def _make_model(*, stream: bool = False): from agentscope.model import OpenAIChatModel + if hasattr(OpenAIChatModel, "Parameters"): + from agentscope.credential import OpenAICredential + + return OpenAIChatModel( + credential=OpenAICredential(api_key="test-api-key"), + model="gpt-4o-mini", + parameters=OpenAIChatModel.Parameters(temperature=0), + stream=stream, + max_retries=0, + ) + return OpenAIChatModel( model_name="gpt-4o-mini", stream=stream, @@ -34,19 +58,30 @@ def _make_model(*, stream: bool = False): def _make_agent(name: str, sys_prompt: str, *, toolkit=None, multi_agent: bool = False): - from agentscope.agent import ReActAgent - from agentscope.formatter import OpenAIChatFormatter, OpenAIMultiAgentFormatter - from agentscope.memory import InMemoryMemory from agentscope.tool import Toolkit - agent = ReActAgent( - name=name, - sys_prompt=sys_prompt, - model=_make_model(), - formatter=OpenAIMultiAgentFormatter() if multi_agent else OpenAIChatFormatter(), - toolkit=toolkit or Toolkit(), - memory=InMemoryMemory(), - ) + if HAS_AGENT_REPLY_API: + from agentscope.agent import Agent + + agent = Agent( + name=name, + system_prompt=sys_prompt, + model=_make_model(), + toolkit=toolkit or Toolkit(), + ) + else: + from agentscope.agent import ReActAgent + from agentscope.formatter import OpenAIChatFormatter, OpenAIMultiAgentFormatter + from agentscope.memory import InMemoryMemory + + agent = ReActAgent( + name=name, + sys_prompt=sys_prompt, + model=_make_model(), + formatter=OpenAIMultiAgentFormatter() if multi_agent else OpenAIChatFormatter(), + toolkit=toolkit or Toolkit(), + memory=InMemoryMemory(), + ) if hasattr(agent, "set_console_output_enabled"): agent.set_console_output_enabled(False) elif hasattr(agent, "disable_console_output"): @@ -66,13 +101,17 @@ async def test_agentscope_simple_agent_run(memory_logger): "You are a concise assistant. Answer in one sentence.", ) - response = await agent( - Msg( + if HAS_USER_MSG: + from agentscope.message import UserMsg + + message = UserMsg("user", "Say hello in exactly two words.") + else: + message = Msg( name="user", content="Say hello in exactly two words.", role="user", ) - ) + response = await (agent.reply(message) if HAS_AGENT_REPLY_API else agent(message)) assert response is not None @@ -93,6 +132,7 @@ async def test_agentscope_simple_agent_run(memory_logger): assert agent_span["span_id"] in llm_spans[0]["span_parents"] +@pytest.mark.skipif(IS_AGENTSCOPE_V2, reason="AgentScope 2.x removed the pipeline module") @pytest.mark.vcr @pytest.mark.asyncio async def test_agentscope_sequential_pipeline_creates_parent_span(memory_logger): @@ -127,6 +167,7 @@ async def test_agentscope_sequential_pipeline_creates_parent_span(memory_logger) assert pipeline_span["span_id"] in bob_span["span_parents"] +@pytest.mark.skipif(IS_AGENTSCOPE_V2, reason="AgentScope 2.x removed execute_python_code") @pytest.mark.vcr @pytest.mark.asyncio async def test_agentscope_tool_use_creates_tool_span(memory_logger): @@ -289,6 +330,35 @@ async def _stream(): assert duration_ms >= 200, f"Span duration {duration_ms:.0f}ms is too short; span ended before stream was consumed" +@pytest.mark.skipif(not IS_AGENTSCOPE_V2, reason="AgentScope 2.x Toolkit.call_tool API") +@pytest.mark.vcr +@pytest.mark.asyncio +async def test_agentscope_v2_toolkit_call_tool_creates_tool_span(memory_logger): + from agentscope.message import TextBlock, ToolCallBlock + from agentscope.state import AgentState + from agentscope.tool import FunctionTool, ToolChunk, Toolkit + + assert not memory_logger.pop() + + def answer(): + return ToolChunk(content=[TextBlock(text="42")]) + + toolkit = Toolkit(tools=[FunctionTool(answer, name="answer")]) + + stream = await toolkit.call_tool( + ToolCallBlock(id="test-call", name="answer", input="{}"), + AgentState(), + ) + chunks = [chunk async for chunk in stream] + + assert chunks + + spans = memory_logger.pop() + tool_span = next(span for span in spans if _span_type(span) == "tool") + assert tool_span["span_attributes"]["name"] == "answer.execute" + assert tool_span["input"]["tool_name"] == "answer" + + class TestAutoInstrumentAgentScope: def test_auto_instrument_agentscope(self): verify_autoinstrument_script("test_auto_agentscope.py") diff --git a/py/src/braintrust/integrations/agentscope/tracing.py b/py/src/braintrust/integrations/agentscope/tracing.py index d2370ef5..06bea174 100644 --- a/py/src/braintrust/integrations/agentscope/tracing.py +++ b/py/src/braintrust/integrations/agentscope/tracing.py @@ -1,6 +1,7 @@ """AgentScope-specific span creation and stream aggregation.""" import contextlib +import inspect from contextlib import aclosing from typing import Any @@ -68,7 +69,7 @@ def _model_provider_name(instance: Any) -> str: def _model_metadata(instance: Any) -> dict[str, Any]: return clean_nones( { - "model": getattr(instance, "model_name", None), + "model": getattr(instance, "model_name", None) or getattr(instance, "model", None), "provider": _model_provider_name(instance), "model_class": instance.__class__.__name__, } @@ -142,6 +143,8 @@ def _field_value(data: Any, key: str) -> Any: def _tool_name(tool_call: Any) -> str: + if isinstance(tool_call, str): + return tool_call if isinstance(tool_call, dict): return str(tool_call.get("name") or "unknown_tool") return str(getattr(tool_call, "name", "unknown_tool")) @@ -231,7 +234,9 @@ async def _toolkit_call_tool_function_wrapper(wrapped: Any, instance: Any, args: ) ) try: - result = await wrapped(*args, **kwargs) + result = wrapped(*args, **kwargs) + if inspect.isawaitable(result): + result = await result if _is_async_iterator(result): return _deferred_stream_trace(result, span, stack, lambda s, chunk: s.log(output=chunk)) diff --git a/py/src/braintrust/integrations/auto_test_scripts/test_auto_agentscope.py b/py/src/braintrust/integrations/auto_test_scripts/test_auto_agentscope.py index 2f3c30da..482ba9e4 100644 --- a/py/src/braintrust/integrations/auto_test_scripts/test_auto_agentscope.py +++ b/py/src/braintrust/integrations/auto_test_scripts/test_auto_agentscope.py @@ -1,39 +1,68 @@ """Test auto_instrument for AgentScope.""" +# pylint: disable=import-error,no-name-in-module,no-value-for-parameter,no-member + +import asyncio +import importlib from braintrust.auto import auto_instrument from braintrust.integrations.test_utils import autoinstrument_test_context -results = auto_instrument() +results = auto_instrument(openai=False) assert results.get("agentscope") == True, "auto_instrument should return True for agentscope" -results2 = auto_instrument() +results2 = auto_instrument(openai=False) assert results2.get("agentscope") == True, "auto_instrument should still return True on second call" -from agentscope.agent import AgentBase, ReActAgent -from agentscope.formatter import OpenAIChatFormatter -from agentscope.memory import InMemoryMemory -from agentscope.message import Msg -from agentscope.model import OpenAIChatModel -from agentscope.pipeline import sequential_pipeline -from agentscope.tool import Toolkit - - -try: - from agentscope.pipeline import fanout_pipeline -except ImportError: - fanout_pipeline = None - - -assert hasattr(AgentBase.__call__, "__wrapped__"), "AgentBase.__call__ should be wrapped" -assert hasattr(sequential_pipeline, "__wrapped__"), "sequential_pipeline should be wrapped" -if fanout_pipeline is not None: - assert hasattr(fanout_pipeline, "__wrapped__"), "fanout_pipeline should be wrapped" -assert hasattr(Toolkit.call_tool_function, "__wrapped__"), "Toolkit.call_tool_function should be wrapped" -assert hasattr(OpenAIChatModel.__call__, "__wrapped__"), "OpenAIChatModel.__call__ should be wrapped" - +agent_module = importlib.import_module("agentscope.agent") +HAS_AGENT_REPLY_API = hasattr(agent_module, "Agent") + +if HAS_AGENT_REPLY_API: + from agentscope.agent import Agent + from agentscope.credential import OpenAICredential + from agentscope.message import UserMsg + from agentscope.model import OpenAIChatModel + from agentscope.tool import Toolkit + + assert hasattr(Agent.reply, "__wrapped__"), "Agent.reply should be wrapped" + assert hasattr(Toolkit.call_tool, "__wrapped__"), "Toolkit.call_tool should be wrapped" + assert hasattr(OpenAIChatModel.__call__, "__wrapped__"), "OpenAIChatModel.__call__ should be wrapped" + + model = OpenAIChatModel( + credential=OpenAICredential(api_key="test-api-key"), + model="gpt-4o-mini", + parameters=OpenAIChatModel.Parameters(temperature=0), + stream=False, + max_retries=0, + ) + agent = Agent( + name="Test Agent", + system_prompt="You are a helpful assistant. Be brief.", + model=model, + toolkit=Toolkit(), + ) + message = UserMsg("user", "Say hello in exactly two words.") +else: + from agentscope.agent import AgentBase, ReActAgent + from agentscope.formatter import OpenAIChatFormatter + from agentscope.memory import InMemoryMemory + from agentscope.message import Msg + from agentscope.model import OpenAIChatModel + from agentscope.pipeline import sequential_pipeline + from agentscope.tool import Toolkit + + try: + from agentscope.pipeline import fanout_pipeline + except ImportError: + fanout_pipeline = None + + assert hasattr(AgentBase.__call__, "__wrapped__"), "AgentBase.__call__ should be wrapped" + assert hasattr(sequential_pipeline, "__wrapped__"), "sequential_pipeline should be wrapped" + if fanout_pipeline is not None: + assert hasattr(fanout_pipeline, "__wrapped__"), "fanout_pipeline should be wrapped" + assert hasattr(Toolkit.call_tool_function, "__wrapped__"), "Toolkit.call_tool_function should be wrapped" + assert hasattr(OpenAIChatModel.__call__, "__wrapped__"), "OpenAIChatModel.__call__ should be wrapped" -with autoinstrument_test_context("test_auto_agentscope", integration="agentscope") as memory_logger: agent = ReActAgent( name="Test Agent", sys_prompt="You are a helpful assistant. Be brief.", @@ -45,22 +74,19 @@ toolkit=Toolkit(), memory=InMemoryMemory(), ) - if hasattr(agent, "set_console_output_enabled"): - agent.set_console_output_enabled(False) - elif hasattr(agent, "disable_console_output"): - agent.disable_console_output() - - response = agent( - Msg( - name="user", - content="Say hi in two words.", - role="user", - ) + message = Msg( + name="user", + content="Say hello in exactly two words.", + role="user", ) - import asyncio +if hasattr(agent, "set_console_output_enabled"): + agent.set_console_output_enabled(False) +elif hasattr(agent, "disable_console_output"): + agent.disable_console_output() - result = asyncio.run(response) +with autoinstrument_test_context("test_auto_agentscope", integration="agentscope") as memory_logger: + result = asyncio.run(agent.reply(message) if HAS_AGENT_REPLY_API else agent(message)) assert result is not None spans = memory_logger.pop()