-
Notifications
You must be signed in to change notification settings - Fork 105
feat: Add adapter for CrewAI #359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
bc4acbb to
e9e510c
Compare
e9e510c to
bca1c50
Compare
|
Due to incompatibility between the current Reference issue: crewAIInc/crewAI#4079 As for the incompatibility with Otel, we should separate ms-agent-framework and crewAI into two optional installation groups to reduce coupling and minimize potential conflicts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds comprehensive CrewAI framework adapter support to AgentScope Runtime, enabling developers to build collaborative autonomous agents with multi-turn conversations, session memory, and streaming responses.
Key Changes:
- Adds CrewAI adapter implementation with message conversion, streaming, memory, and tool adapters
- Integrates CrewAI as a supported framework in the engine (runner, constants, and app registration)
- Includes comprehensive integration tests and bilingual documentation
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
tests/integrated/test_crewai_agent_app.py |
Integration tests for CrewAI adapter with streaming, OpenAI-compatible mode, and multi-turn conversation tests |
src/agentscope_runtime/engine/runner.py |
Adds CrewAI message stream adapter integration in the streaming query handler |
src/agentscope_runtime/engine/constant.py |
Registers "crewai" as a supported framework |
src/agentscope_runtime/engine/app/agent_app.py |
Adds "crewai" to allowed frameworks in query decorator |
src/agentscope_runtime/adapters/crewai/message.py |
Converts AgentScope runtime messages to CrewAI-compatible format |
src/agentscope_runtime/adapters/crewai/stream.py |
Adapts CrewAI streaming chunks to runtime message format with reasoning/message mode parsing |
src/agentscope_runtime/adapters/crewai/memory/_external_memory_adapter.py |
CrewAI external memory adapter using SessionHistoryService for persistence |
src/agentscope_runtime/adapters/crewai/tool/tool.py |
Tool adapter converting runtime tools to CrewAI BaseTool format |
cookbook/en/crewai_guidelines.md |
English documentation guide for CrewAI integration |
cookbook/zh/crewai_guidelines.md |
Chinese documentation guide for CrewAI integration |
pyproject.toml |
Adds crewai>=1.6.1 dependency to ext optional dependencies |
README.md / README_zh.md |
Updates framework support table with CrewAI entry |
cookbook/_toc.yml |
Adds CrewAI guidelines to documentation table of contents |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,280 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # tests/api/test_crewai_agent_app.py | |||
Copilot
AI
Jan 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file path comment is incorrect. It states "tests/api/test_crewai_agent_app.py" but the actual file is located at "tests/integrated/test_crewai_agent_app.py". This should be updated to match the actual file location.
| # tests/api/test_crewai_agent_app.py | |
| # tests/integrated/test_crewai_agent_app.py |
| f"Investigate the following user query: '{user_question}'" | ||
| ), | ||
| expected_output=( | ||
| "A comprehensive yet easy-to-read answer" |
Copilot
AI
Jan 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space in the concatenated string. The expected output should have a space between "answer" and "that" for readability.
| "A comprehensive yet easy-to-read answer" | |
| "A comprehensive yet easy-to-read answer " |
| agno_message = await formatter.format(as_msgs) | ||
|
|
||
| return agno_message if raw_list else agno_message[0] |
Copilot
AI
Jan 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name "agno_message" is misleading. This is a CrewAI adapter, not an Agno adapter, so the variable should be named "crewai_message" or a more generic name like "formatted_message" to accurately reflect its purpose.
| agno_message = await formatter.format(as_msgs) | |
| return agno_message if raw_list else agno_message[0] | |
| crewai_message = await formatter.format(as_msgs) | |
| return crewai_message if raw_list else crewai_message[0] |
| agno_message = await formatter.format(as_msgs) | ||
|
|
||
| return agno_message if raw_list else agno_message[0] |
Copilot
AI
Jan 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name "agno_message" is misleading. This is a CrewAI adapter, not an Agno adapter, so the variable should be named "crewai_message" or a more generic name like "formatted_message" to accurately reflect its purpose.
| agno_message = await formatter.format(as_msgs) | |
| return agno_message if raw_list else agno_message[0] | |
| crewai_message = await formatter.format(as_msgs) | |
| return crewai_message if raw_list else crewai_message[0] |
|
|
||
| break | ||
| # elif event.chunk_type == StreamChunkType.TOOL_CALL: | ||
| # Not support now |
Copilot
AI
Jan 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grammatical error in the comment. "Not support now" should be "Not supported now" for correct grammar.
| # Not support now | |
| # Not supported now |
|
|
||
| async def message_to_crewai_message( | ||
| messages: Union[Message, List[Message]], | ||
| ) -> Union[dict, List[dict]]: |
Copilot
AI
Jan 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing docstring for the function. Based on similar adapters (e.g., the Agno adapter), this function should include a docstring that explains what the function does, its arguments, and return values. For example: "Convert AgentScope runtime Message(s) to CrewAI Message(s)."
| ) -> Union[dict, List[dict]]: | |
| ) -> Union[dict, List[dict]]: | |
| """ | |
| Convert AgentScope runtime Message(s) to CrewAI-compatible message dict(s). | |
| This function first converts the given AgentScope runtime ``Message`` or list | |
| of ``Message`` objects into AgentScope messages, and then uses | |
| ``OpenAIChatFormatter`` to produce CrewAI-compatible message dictionaries. | |
| Args: | |
| messages: A single ``Message`` instance or a list of ``Message`` instances | |
| from the AgentScope runtime. | |
| Returns: | |
| A single dictionary if ``messages`` is a single ``Message``; otherwise a | |
| list of dictionaries, each representing a CrewAI-compatible message. | |
| """ |
| obs_json = json.loads(obs_text) | ||
| if isinstance(obs_json, dict) and "request_id" in obs_json: | ||
| call_id = obs_json["request_id"] | ||
| except (json.JSONDecodeError, TypeError): |
Copilot
AI
Jan 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'except' clause does nothing but pass and there is no explanatory comment.
| except (json.JSONDecodeError, TypeError): | |
| except (json.JSONDecodeError, TypeError): | |
| # If the observation text is not valid JSON or does not have the | |
| # expected structure, we ignore the error and fall back to | |
| # generating a new call_id below. |
| ].lower() | ||
| if "alice" in text_content: | ||
| found_name = True | ||
| except Exception: |
Copilot
AI
Jan 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'except' clause does nothing but pass and there is no explanatory comment.
| except Exception: | |
| except Exception: | |
| # Ignore malformed or unexpected output chunks; only well-formed | |
| # responses should contribute to found_name in this streaming test. |
Description
Support CrewAI
Type of Change
Component(s) Affected
Checklist