From c3bf08ac8d6726e0e38fc0f6083f2ae178278082 Mon Sep 17 00:00:00 2001 From: wehub-resource-sync Date: Mon, 13 Jul 2026 12:39:27 +0800 Subject: [PATCH] chore: import upstream snapshot with attribution --- .gemini/styleguide.md | 96 + .github/ISSUE_TEMPLATE/bug_report.yml | 67 + .github/ISSUE_TEMPLATE/config.yml | 8 + .github/PULL_REQUEST_TEMPLATE.md | 25 + .github/copilot-instructions.md | 97 + .github/scripts/update_news.py | 152 + .github/workflows/github-monitor.yml | 63 + .github/workflows/k8s-workspace.yml | 81 + .github/workflows/pr-title-check.yml | 49 + .github/workflows/pre-commit.yml | 35 + .github/workflows/publish-pypi.yml | 43 + .github/workflows/stale.yml | 35 + .github/workflows/unittest.yml | 32 + .github/workflows/update_news.yml | 39 + .github/workflows/web-ui.yml | 46 + .gitignore | 166 + .pre-commit-config.yaml | 116 + CONTRIBUTING.md | 420 + CONTRIBUTING_zh.md | 298 + LICENSE | 391 + README.md | 277 + README.wehub.md | 7 + README_zh.md | 300 + assets/images/agentscope.png | Bin 0 -> 652417 bytes assets/images/bg_tool.gif | Bin 0 -> 286509 bytes assets/images/dingtalk_qr_code.png | Bin 0 -> 94295 bytes assets/images/permission_bypass.gif | Bin 0 -> 683204 bytes assets/images/task.gif | Bin 0 -> 1686547 bytes assets/images/team.gif | Bin 0 -> 1338298 bytes docs/NEWS.md | 12 + docs/NEWS_zh.md | 12 + docs/changelog.md | 98 + docs/roadmap.md | 121 + examples/agent_service/README.md | 78 + examples/agent_service/main.py | 131 + .../long_term_memory/agentic_memory/README.md | 83 + .../long_term_memory/agentic_memory/main.py | 238 + examples/long_term_memory/mem0/README.md | 387 + examples/long_term_memory/mem0/oss_demo.py | 325 + examples/long_term_memory/reme/README.md | 203 + examples/long_term_memory/reme/reme_demo.py | 336 + examples/rag/README.md | 139 + examples/rag/index_and_search.py | 183 + examples/rag/integrate_with_agent.py | 198 + examples/web_ui/.gitignore | 42 + examples/web_ui/.husky/pre-commit | 1 + examples/web_ui/.prettierignore | 38 + examples/web_ui/.prettierrc | 10 + examples/web_ui/backend/package.json | 22 + examples/web_ui/backend/src/index.ts | 16 + examples/web_ui/backend/tsconfig.json | 16 + examples/web_ui/frontend/README.md | 73 + examples/web_ui/frontend/components.json | 25 + examples/web_ui/frontend/eslint.config.js | 53 + examples/web_ui/frontend/index.html | 13 + examples/web_ui/frontend/package.json | 66 + .../web_ui/frontend/public/agentscope.svg | 1 + examples/web_ui/frontend/src/App.tsx | 84 + examples/web_ui/frontend/src/api/agent.ts | 23 + examples/web_ui/frontend/src/api/chat.ts | 25 + examples/web_ui/frontend/src/api/client.ts | 119 + .../web_ui/frontend/src/api/credential.ts | 23 + examples/web_ui/frontend/src/api/index.ts | 9 + .../web_ui/frontend/src/api/knowledgeBase.ts | 179 + examples/web_ui/frontend/src/api/model.ts | 10 + examples/web_ui/frontend/src/api/schedule.ts | 24 + examples/web_ui/frontend/src/api/session.ts | 104 + examples/web_ui/frontend/src/api/types.ts | 736 ++ examples/web_ui/frontend/src/api/workspace.ts | 38 + .../frontend/src/assets/images/agentscope.svg | 1 + .../src/assets/images/line-corner.svg | 3 + .../src/assets/images/line-vertical.svg | 3 + .../web_ui/frontend/src/assets/images/mcp.svg | 6 + .../src/components/badge/InputTypeBadges.tsx | 65 + .../src/components/badge/StatusBadge.tsx | 55 + .../src/components/chat/ChatContent.tsx | 137 + .../src/components/chat/ConfirmCard.tsx | 150 + .../frontend/src/components/chat/Empty.tsx | 30 + .../src/components/chat/FileAttachment.tsx | 119 + .../src/components/chat/MessageBubble.tsx | 611 ++ .../src/components/chat/SubagentHitlCard.tsx | 56 + .../src/components/chat/TextInput.tsx | 412 + .../chat/tool-renderers/BashRenderer.tsx | 58 + .../chat/tool-renderers/DefaultRenderer.tsx | 136 + .../chat/tool-renderers/DiffPreview.tsx | 204 + .../chat/tool-renderers/EditRenderer.tsx | 108 + .../chat/tool-renderers/GlobRenderer.tsx | 30 + .../chat/tool-renderers/GrepRenderer.tsx | 30 + .../chat/tool-renderers/ReadRenderer.tsx | 116 + .../tool-renderers/TaskCreateRenderer.tsx | 91 + .../chat/tool-renderers/WriteRenderer.tsx | 93 + .../chat/tool-renderers/_shared.tsx | 207 + .../components/chat/tool-renderers/index.ts | 80 + .../components/chat/tool-renderers/types.ts | 23 + .../src/components/dialog/AddSkillDialog.tsx | 83 + .../src/components/dialog/AgentDialog.tsx | 137 + .../dialog/CreateCredentialDialog.tsx | 152 + .../dialog/CreateKnowledgeBaseDialog.tsx | 246 + .../src/components/dialog/DeleteDialog.tsx | 70 + .../src/components/dialog/EditAgentDialog.tsx | 151 + .../dialog/EditCredentialDialog.tsx | 118 + .../dialog/EditKnowledgeBaseDialog.tsx | 139 + .../src/components/dialog/MCPDialog.tsx | 206 + .../components/dialog/RenameSessionDialog.tsx | 82 + .../drawer/KnowledgeSearchDrawer.tsx | 199 + .../src/components/error/RouteError.tsx | 58 + .../src/components/form/AgentFormFields.tsx | 149 + .../src/components/form/SchemaForm.tsx | 220 + .../knowledge/KnowledgeDocumentsPanel.tsx | 481 + .../src/components/layout/AppLayout.tsx | 17 + .../src/components/layout/AppSidebar.tsx | 154 + .../components/panel/KnowledgeBasePanel.tsx | 156 + .../src/components/panel/McpPanel.tsx | 146 + .../src/components/panel/PanelDock.tsx | 183 + .../src/components/panel/PanelEmpty.tsx | 46 + .../src/components/panel/PermissionPanel.tsx | 213 + .../src/components/panel/SkillPanel.tsx | 131 + .../src/components/panel/TaskPanel.tsx | 163 + .../KnowledgeBaseParametersPopover.tsx | 277 + .../popover/ModelParametersPopover.tsx | 616 ++ .../src/components/select/AgentSelect.tsx | 109 + .../src/components/select/DimensionSelect.tsx | 74 + .../src/components/select/EmbeddingSelect.tsx | 146 + .../src/components/select/LlmSelect.tsx | 152 + .../select/PermissionModeSelect.tsx | 75 + .../src/components/select/TimezoneSelect.tsx | 89 + .../src/components/team/TeamSidebar.tsx | 131 + .../components/tour/ChatTourController.tsx | 61 + .../frontend/src/components/tour/TourCard.tsx | 73 + .../src/components/tour/chatTourSteps.ts | 60 + .../frontend/src/components/ui/alert.tsx | 73 + .../frontend/src/components/ui/badge.tsx | 46 + .../src/components/ui/button-group.tsx | 78 + .../frontend/src/components/ui/button.tsx | 86 + .../frontend/src/components/ui/calendar.tsx | 198 + .../frontend/src/components/ui/card.tsx | 95 + .../frontend/src/components/ui/checkbox.tsx | 27 + .../src/components/ui/collapsible.tsx | 19 + .../frontend/src/components/ui/dialog.tsx | 147 + .../frontend/src/components/ui/drawer.tsx | 120 + .../src/components/ui/dropdown-menu.tsx | 248 + .../frontend/src/components/ui/empty.tsx | 94 + .../frontend/src/components/ui/field.tsx | 227 + .../src/components/ui/input-group.tsx | 143 + .../frontend/src/components/ui/input.tsx | 19 + .../frontend/src/components/ui/item.tsx | 185 + .../web_ui/frontend/src/components/ui/kbd.tsx | 26 + .../frontend/src/components/ui/label.tsx | 19 + .../frontend/src/components/ui/popover.tsx | 72 + .../frontend/src/components/ui/resizable.tsx | 42 + .../frontend/src/components/ui/select.tsx | 183 + .../frontend/src/components/ui/separator.tsx | 28 + .../frontend/src/components/ui/sheet.tsx | 128 + .../frontend/src/components/ui/sidebar.tsx | 673 ++ .../frontend/src/components/ui/skeleton.tsx | 13 + .../frontend/src/components/ui/sonner.tsx | 43 + .../frontend/src/components/ui/spinner.tsx | 15 + .../frontend/src/components/ui/switch.tsx | 31 + .../frontend/src/components/ui/tabs.tsx | 78 + .../frontend/src/components/ui/textarea.tsx | 18 + .../frontend/src/components/ui/toggle.tsx | 44 + .../frontend/src/components/ui/tooltip.tsx | 53 + .../frontend/src/context/AudioContext.tsx | 86 + .../frontend/src/context/UploadContext.tsx | 421 + .../frontend/src/context/uploadTypes.ts | 44 + .../web_ui/frontend/src/hooks/use-mobile.ts | 19 + .../frontend/src/hooks/useAgentSchema.ts | 38 + .../web_ui/frontend/src/hooks/useAgents.ts | 62 + .../frontend/src/hooks/useAvailableModels.ts | 55 + .../src/hooks/useAvailableTTSModels.ts | 61 + examples/web_ui/frontend/src/hooks/useChat.ts | 41 + .../frontend/src/hooks/useCredentials.ts | 62 + .../src/hooks/useDocumentStatusPolling.ts | 136 + .../src/hooks/useKbEmbeddingModels.ts | 39 + .../hooks/useKnowledgeBaseMiddlewareSchema.ts | 72 + .../frontend/src/hooks/useKnowledgeBases.ts | 116 + .../src/hooks/useKnowledgeDocuments.ts | 47 + .../useKnowledgeSupportedContentTypes.ts | 78 + .../web_ui/frontend/src/hooks/useMessages.ts | 491 + .../web_ui/frontend/src/hooks/useModels.ts | 33 + .../web_ui/frontend/src/hooks/useSchedules.ts | 62 + .../web_ui/frontend/src/hooks/useSessions.ts | 76 + .../web_ui/frontend/src/hooks/useSkills.ts | 59 + .../web_ui/frontend/src/hooks/useWorkspace.ts | 114 + examples/web_ui/frontend/src/i18n/index.ts | 25 + .../web_ui/frontend/src/i18n/locales/en.json | 556 + .../web_ui/frontend/src/i18n/locales/zh.json | 556 + examples/web_ui/frontend/src/i18n/useI18n.ts | 5 + examples/web_ui/frontend/src/index.css | 214 + examples/web_ui/frontend/src/lib/api-error.ts | 32 + .../frontend/src/lib/next-navigation-shim.ts | 22 + examples/web_ui/frontend/src/lib/utils.ts | 6 + examples/web_ui/frontend/src/main.tsx | 15 + .../frontend/src/pages/chat/ChatViewport.tsx | 711 ++ .../web_ui/frontend/src/pages/chat/index.tsx | 434 + .../frontend/src/pages/credential/index.tsx | 533 + .../frontend/src/pages/knowledge/index.tsx | 284 + .../src/pages/schedule/calendar-tab-page.tsx | 340 + .../pages/schedule/create-schedule-dialog.tsx | 348 + .../src/pages/schedule/empty-state.tsx | 46 + .../frontend/src/pages/schedule/event.tsx | 9 + .../frontend/src/pages/schedule/index.tsx | 132 + .../src/pages/schedule/list-tab-page.tsx | 141 + .../src/pages/schedule/schedule-card.tsx | 50 + .../pages/schedule/schedule-detail-drawer.tsx | 220 + .../src/pages/schedule/schedule-utils.ts | 50 + .../web_ui/frontend/src/pages/setup/index.tsx | 83 + .../web_ui/frontend/src/types/unidiff.d.ts | 20 + examples/web_ui/frontend/src/utils/common.ts | 96 + .../web_ui/frontend/src/utils/platform.ts | 33 + .../frontend/src/utils/streamingAudio.ts | 387 + examples/web_ui/frontend/src/vite-env.d.ts | 2 + examples/web_ui/frontend/tsconfig.app.json | 32 + examples/web_ui/frontend/tsconfig.json | 10 + examples/web_ui/frontend/tsconfig.node.json | 24 + examples/web_ui/frontend/vite.config.ts | 24 + examples/web_ui/package.json | 36 + examples/web_ui/pnpm-lock.yaml | 9340 +++++++++++++++++ examples/web_ui/pnpm-workspace.yaml | 6 + pyproject.toml | 180 + scripts/model_examples/README.md | 231 + scripts/model_examples/_utils.py | 104 + scripts/model_examples/anthropic_call.py | 191 + .../model_examples/anthropic_multiagent.py | 105 + .../anthropic_multiagent_multimodal.py | 99 + .../model_examples/anthropic_multimodal.py | 154 + scripts/model_examples/dashscope_call.py | 185 + .../model_examples/dashscope_multiagent.py | 101 + .../dashscope_multiagent_multimodal.py | 95 + .../model_examples/dashscope_multimodal.py | 231 + scripts/model_examples/deepseek_call.py | 189 + scripts/model_examples/deepseek_multiagent.py | 102 + scripts/model_examples/gemini_call.py | 191 + scripts/model_examples/gemini_multiagent.py | 105 + .../gemini_multiagent_multimodal.py | 93 + scripts/model_examples/gemini_multimodal.py | 143 + scripts/model_examples/moonshot_call.py | 180 + scripts/model_examples/moonshot_multiagent.py | 101 + .../moonshot_multiagent_multimodal.py | 91 + scripts/model_examples/moonshot_multimodal.py | 137 + scripts/model_examples/ollama_call.py | 188 + scripts/model_examples/ollama_multiagent.py | 96 + .../ollama_multiagent_multimodal.py | 91 + scripts/model_examples/ollama_multimodal.py | 136 + scripts/model_examples/openai_chat_call.py | 191 + .../model_examples/openai_chat_multiagent.py | 100 + .../openai_chat_multiagent_multimodal.py | 90 + .../model_examples/openai_chat_multimodal.py | 194 + .../model_examples/openai_response_call.py | 199 + .../openai_response_multiagent.py | 102 + .../openai_response_multiagent_multimodal.py | 90 + .../openai_response_multimodal.py | 138 + scripts/model_examples/run_tests.py | 585 ++ scripts/model_examples/test.jpeg | Bin 0 -> 496395 bytes scripts/model_examples/xai_call.py | 195 + scripts/model_examples/xai_multiagent.py | 101 + .../xai_multiagent_multimodal.py | 93 + scripts/model_examples/xai_multimodal.py | 140 + src/agentscope/__init__.py | 21 + src/agentscope/_logging.py | 47 + src/agentscope/_utils/__init__.py | 0 src/agentscope/_utils/_audio.py | 42 + src/agentscope/_utils/_common.py | 341 + src/agentscope/_utils/_mixin.py | 9 + src/agentscope/_version.py | 4 + src/agentscope/agent/__init__.py | 6 + src/agentscope/agent/_agent.py | 2836 +++++ src/agentscope/agent/_config.py | 191 + src/agentscope/agent/_utils.py | 17 + src/agentscope/app/__init__.py | 12 + src/agentscope/app/_app.py | 274 + src/agentscope/app/_bus_ops.py | 169 + src/agentscope/app/_lifespan.py | 194 + src/agentscope/app/_manager/__init__.py | 17 + .../app/_manager/_background_task_manager.py | 470 + .../app/_manager/_cancel_dispatcher.py | 246 + .../app/_manager/_chat_run_registry.py | 134 + .../app/_manager/_scheduler/__init__.py | 8 + .../_manager/_scheduler/_scheduler_manager.py | 432 + .../_manager/_scheduler/_tools/__init__.py | 14 + .../_scheduler/_tools/_schedule_create.py | 253 + .../_scheduler/_tools/_schedule_delete.py | 154 + .../_scheduler/_tools/_schedule_list.py | 115 + .../_scheduler/_tools/_schedule_view.py | 138 + .../app/_manager/_wakeup_dispatcher.py | 375 + src/agentscope/app/_router/__init__.py | 23 + src/agentscope/app/_router/_agent.py | 318 + src/agentscope/app/_router/_chat.py | 160 + src/agentscope/app/_router/_credential.py | 195 + src/agentscope/app/_router/_knowledge_base.py | 575 + src/agentscope/app/_router/_model.py | 40 + src/agentscope/app/_router/_schedule.py | 253 + .../app/_router/_schema/__init__.py | 116 + src/agentscope/app/_router/_schema/_agent.py | 139 + src/agentscope/app/_router/_schema/_chat.py | 45 + .../app/_router/_schema/_credential.py | 42 + .../app/_router/_schema/_knowledge_base.py | 274 + src/agentscope/app/_router/_schema/_mcp.py | 127 + src/agentscope/app/_router/_schema/_model.py | 21 + .../app/_router/_schema/_schedule.py | 118 + .../app/_router/_schema/_session.py | 238 + .../app/_router/_schema/_tts_model.py | 23 + src/agentscope/app/_router/_session.py | 837 ++ src/agentscope/app/_router/_tts_model.py | 40 + src/agentscope/app/_router/_workspace.py | 220 + src/agentscope/app/_service/__init__.py | 40 + src/agentscope/app/_service/_access.py | 473 + src/agentscope/app/_service/_chat.py | 690 ++ src/agentscope/app/_service/_embedding.py | 126 + src/agentscope/app/_service/_index_sweeper.py | 150 + .../app/_service/_index_task_consumer.py | 216 + src/agentscope/app/_service/_index_worker.py | 534 + .../app/_service/_knowledge_base.py | 545 + src/agentscope/app/_service/_model.py | 57 + .../app/_service/_projectors/__init__.py | 13 + .../_service/_projectors/_subagent_hitl.py | 275 + src/agentscope/app/_service/_session.py | 687 ++ .../app/_service/_session_projection.py | 186 + src/agentscope/app/_service/_toolkit.py | 251 + src/agentscope/app/_service/_tts_model.py | 65 + src/agentscope/app/_tool/__init__.py | 46 + src/agentscope/app/_tool/_agent_create.py | 574 + src/agentscope/app/_tool/_agent_invite.py | 566 + src/agentscope/app/_tool/_constants.py | 19 + src/agentscope/app/_tool/_team_create.py | 145 + src/agentscope/app/_tool/_team_delete.py | 130 + src/agentscope/app/_tool/_team_say.py | 352 + src/agentscope/app/_tool/_team_tool_base.py | 99 + src/agentscope/app/_types.py | 189 + src/agentscope/app/access/__init__.py | 30 + src/agentscope/app/access/_policy.py | 163 + src/agentscope/app/deps.py | 315 + src/agentscope/app/message_bus/__init__.py | 14 + src/agentscope/app/message_bus/_base.py | 810 ++ .../app/message_bus/_in_memory_message_bus.py | 470 + src/agentscope/app/message_bus/_keys.py | 240 + .../app/message_bus/_redis_message_bus.py | 641 ++ src/agentscope/app/middleware/__init__.py | 16 + .../app/middleware/_inbox_middleware.py | 136 + .../app/middleware/_protocol/__init__.py | 10 + .../app/middleware/_protocol/_agui.py | 257 + .../app/middleware/_protocol/_base.py | 244 + .../middleware/_state_change_middleware.py | 196 + .../middleware/_tool_offload_middleware.py | 397 + src/agentscope/app/rag/__init__.py | 92 + src/agentscope/app/rag/blob_store/__init__.py | 12 + src/agentscope/app/rag/blob_store/_base.py | 158 + src/agentscope/app/rag/blob_store/_local.py | 145 + src/agentscope/app/rag/blob_store/_s3.py | 276 + .../app/rag/index_worker/__init__.py | 198 + .../app/rag/index_worker/__main__.py | 108 + .../rag/knowledge_base_manager/__init__.py | 33 + .../app/rag/knowledge_base_manager/_base.py | 302 + .../_collection_per_kb.py | 202 + .../_dimension_policy.py | 154 + .../app/rag/knowledge_base_manager/_errors.py | 59 + src/agentscope/app/storage/__init__.py | 57 + src/agentscope/app/storage/_base.py | 920 ++ src/agentscope/app/storage/_model/__init__.py | 48 + src/agentscope/app/storage/_model/_agent.py | 130 + src/agentscope/app/storage/_model/_base.py | 26 + .../app/storage/_model/_credential.py | 17 + .../app/storage/_model/_knowledge_base.py | 46 + .../app/storage/_model/_knowledge_document.py | 139 + .../app/storage/_model/_schedule.py | 117 + src/agentscope/app/storage/_model/_session.py | 176 + src/agentscope/app/storage/_model/_team.py | 122 + src/agentscope/app/storage/_model/_user.py | 8 + src/agentscope/app/storage/_redis_storage.py | 1770 ++++ src/agentscope/app/storage/_utils.py | 107 + .../app/workspace_manager/__init__.py | 20 + src/agentscope/app/workspace_manager/_base.py | 138 + .../_docker_workspace_manager.py | 397 + .../_e2b_workspace_manager.py | 408 + .../_k8s_workspace_manager.py | 354 + .../_local_workspace_manager.py | 210 + .../_opensandbox_workspace_manager.py | 356 + src/agentscope/credential/__init__.py | 27 + src/agentscope/credential/_anthropic.py | 39 + src/agentscope/credential/_base.py | 96 + src/agentscope/credential/_dashscope.py | 67 + src/agentscope/credential/_deepseek.py | 41 + src/agentscope/credential/_factory.py | 116 + src/agentscope/credential/_gemini.py | 41 + src/agentscope/credential/_kimi.py | 0 src/agentscope/credential/_moonshot.py | 41 + src/agentscope/credential/_ollama.py | 45 + src/agentscope/credential/_openai.py | 64 + src/agentscope/credential/_xai.py | 43 + src/agentscope/embedding/__init__.py | 27 + src/agentscope/embedding/_cache_base.py | 63 + .../embedding/_dashscope/__init__.py | 8 + src/agentscope/embedding/_dashscope/_model.py | 529 + .../_models/multimodal-embedding-v1.yaml | 16 + .../_models/qwen2.5-vl-embedding.yaml | 20 + .../_models/qwen3-vl-embedding.yaml | 20 + .../_dashscope/_models/text-embedding-v3.yaml | 14 + .../_dashscope/_models/text-embedding-v4.yaml | 14 + .../tongyi-embedding-vision-flash.yaml | 21 + .../_models/tongyi-embedding-vision-plus.yaml | 21 + src/agentscope/embedding/_embedding_base.py | 449 + .../embedding/_embedding_model_card.py | 179 + .../embedding/_embedding_response.py | 32 + src/agentscope/embedding/_embedding_usage.py | 20 + src/agentscope/embedding/_file_cache.py | 187 + src/agentscope/embedding/_gemini/__init__.py | 8 + src/agentscope/embedding/_gemini/_model.py | 485 + .../_gemini/_models/gemini-embedding-001.yaml | 14 + .../_gemini/_models/gemini-embedding-2.yaml | 20 + src/agentscope/embedding/_ollama/__init__.py | 8 + src/agentscope/embedding/_ollama/_model.py | 132 + src/agentscope/embedding/_openai/__init__.py | 8 + src/agentscope/embedding/_openai/_model.py | 179 + .../_models/text-embedding-3-large.yaml | 14 + .../_models/text-embedding-3-small.yaml | 14 + src/agentscope/event/__init__.py | 75 + src/agentscope/event/_event.py | 548 + src/agentscope/exception/__init__.py | 22 + src/agentscope/exception/_base.py | 27 + src/agentscope/exception/_tool.py | 20 + src/agentscope/formatter/__init__.py | 62 + .../formatter/_anthropic_formatter.py | 533 + .../formatter/_dashscope_formatter.py | 563 + .../formatter/_deepseek_formatter.py | 313 + src/agentscope/formatter/_formatter_base.py | 217 + src/agentscope/formatter/_gemini_formatter.py | 445 + .../formatter/_moonshot_formatter.py | 417 + src/agentscope/formatter/_ollama_formatter.py | 452 + src/agentscope/formatter/_openai_formatter.py | 513 + .../formatter/_openai_response_formatter.py | 480 + src/agentscope/formatter/_xai_formatter.py | 485 + src/agentscope/mcp/__init__.py | 13 + src/agentscope/mcp/_config.py | 64 + src/agentscope/mcp/_mcp_client.py | 438 + src/agentscope/message/__init__.py | 39 + src/agentscope/message/_base.py | 618 ++ src/agentscope/message/_block.py | 203 + src/agentscope/middleware/__init__.py | 24 + src/agentscope/middleware/_base.py | 250 + src/agentscope/middleware/_budget.py | 207 + .../middleware/_longterm_memory/__init__.py | 12 + .../_agentic_memory/__init__.py | 6 + .../_agentic_memory/_middleware.py | 802 ++ .../_longterm_memory/_mem0/__init__.py | 6 + .../_mem0/_agentscope_adapter.py | 439 + .../_longterm_memory/_mem0/_middleware.py | 741 ++ .../_longterm_memory/_mem0/_tools.py | 270 + .../_longterm_memory/_mem0/_utils.py | 76 + .../_longterm_memory/_reme/__init__.py | 16 + .../_longterm_memory/_reme/_middleware.py | 564 + .../_longterm_memory/_reme/_tools.py | 182 + .../_longterm_memory/_reme/_utils.py | 104 + src/agentscope/middleware/_rag.py | 765 ++ .../middleware/_tracing/__init__.py | 8 + .../middleware/_tracing/_attributes.py | 201 + .../middleware/_tracing/_converter.py | 121 + .../middleware/_tracing/_extractor.py | 608 ++ src/agentscope/middleware/_tracing/_setup.py | 19 + src/agentscope/middleware/_tracing/_trace.py | 348 + src/agentscope/middleware/_tracing/_utils.py | 78 + src/agentscope/middleware/_tts_middleware.py | 179 + src/agentscope/model/__init__.py | 34 + src/agentscope/model/_anthropic/__init__.py | 9 + src/agentscope/model/_anthropic/_model.py | 545 + .../_anthropic/_models/claude-haiku-4-5.yaml | 21 + .../_anthropic/_models/claude-opus-4-5.yaml | 21 + .../_anthropic/_models/claude-opus-4-6.yaml | 21 + .../_anthropic/_models/claude-opus-4-7.yaml | 21 + .../_anthropic/_models/claude-opus-4-8.yaml | 21 + .../_anthropic/_models/claude-sonnet-4-5.yaml | 21 + .../_anthropic/_models/claude-sonnet-4-6.yaml | 21 + src/agentscope/model/_base.py | 653 ++ src/agentscope/model/_dashscope/__init__.py | 9 + src/agentscope/model/_dashscope/_model.py | 581 + .../model/_dashscope/_models/qwen-long.yaml | 21 + .../model/_dashscope/_models/qwen-plus.yaml | 20 + .../_dashscope/_models/qwen3.5-omni-plus.yaml | 38 + .../_dashscope/_models/qwen3.5-plus.yaml | 28 + .../_models/qwen3.6-max-preview.yaml | 20 + .../_dashscope/_models/qwen3.6-plus.yaml | 28 + .../model/_dashscope/_models/qwen3.7-max.yaml | 20 + src/agentscope/model/_deepseek/__init__.py | 9 + src/agentscope/model/_deepseek/_model.py | 478 + .../_deepseek/_models/deepseek-chat.yaml | 17 + .../_deepseek/_models/deepseek-reasoner.yaml | 18 + .../_deepseek/_models/deepseek-v4-flash.yaml | 18 + .../_deepseek/_models/deepseek-v4-pro.yaml | 18 + src/agentscope/model/_gemini/__init__.py | 9 + src/agentscope/model/_gemini/_model.py | 560 + .../_gemini/_models/gemini-2.5-flash.yaml | 25 + .../model/_gemini/_models/gemini-2.5-pro.yaml | 25 + .../_models/gemini-3-flash-preview.yaml | 25 + .../_models/gemini-3.1-pro-preview.yaml | 25 + src/agentscope/model/_model_card.py | 159 + src/agentscope/model/_model_response.py | 350 + src/agentscope/model/_model_usage.py | 32 + src/agentscope/model/_moonshot/__init__.py | 8 + src/agentscope/model/_moonshot/_model.py | 452 + .../model/_moonshot/_models/kimi-k2.5.yaml | 22 + .../model/_moonshot/_models/kimi-k2.6.yaml | 27 + .../_moonshot/_models/moonshot-v1-128k.yaml | 16 + .../_moonshot/_models/moonshot-v1-32k.yaml | 16 + .../_moonshot/_models/moonshot-v1-8k.yaml | 16 + src/agentscope/model/_ollama/__init__.py | 9 + src/agentscope/model/_ollama/_model.py | 350 + .../_ollama/_models/deepseek-r1-14b.yaml | 17 + .../model/_ollama/_models/llama4.yaml | 22 + .../model/_ollama/_models/qwen3-14b.yaml | 17 + .../model/_ollama/_models/qwen3.5-9b.yaml | 17 + src/agentscope/model/_openai_chat/__init__.py | 8 + src/agentscope/model/_openai_chat/_model.py | 670 ++ .../_openai_chat/_models/gpt-4.1-mini.yaml | 28 + .../_openai_chat/_models/gpt-4.1-nano.yaml | 28 + .../model/_openai_chat/_models/gpt-4.1.yaml | 26 + .../_openai_chat/_models/gpt-4o-mini.yaml | 28 + .../model/_openai_chat/_models/gpt-4o.yaml | 28 + .../model/_openai_chat/_models/gpt-5.4.yaml | 26 + .../model/_openai_chat/_models/gpt-5.5.yaml | 30 + .../_openai_chat/_models/gpt-audio-mini.yaml | 31 + .../model/_openai_chat/_models/o3.yaml | 25 + .../model/_openai_chat/_models/o4-mini.yaml | 25 + .../model/_openai_response/__init__.py | 8 + .../model/_openai_response/_model.py | 485 + .../_models/gpt-4.1-mini.yaml | 24 + .../_models/gpt-4.1-nano.yaml | 24 + .../_openai_response/_models/gpt-4.1.yaml | 24 + .../_openai_response/_models/gpt-4o-mini.yaml | 24 + .../_openai_response/_models/gpt-4o.yaml | 24 + .../_openai_response/_models/gpt-5.4.yaml | 22 + .../_openai_response/_models/gpt-5.5.yaml | 26 + .../model/_openai_response/_models/o3.yaml | 24 + .../_openai_response/_models/o4-mini.yaml | 24 + src/agentscope/model/_xai/__init__.py | 9 + src/agentscope/model/_xai/_model.py | 454 + .../model/_xai/_models/grok-3-fast.yaml | 19 + .../model/_xai/_models/grok-3-mini.yaml | 17 + src/agentscope/model/_xai/_models/grok-3.yaml | 19 + .../model/_xai/_models/grok-4.3.yaml | 19 + src/agentscope/permission/__init__.py | 18 + src/agentscope/permission/_context.py | 46 + src/agentscope/permission/_decision.py | 68 + src/agentscope/permission/_engine.py | 729 ++ src/agentscope/permission/_rule.py | 36 + src/agentscope/permission/_types.py | 102 + src/agentscope/py.typed | 0 src/agentscope/rag/__init__.py | 49 + src/agentscope/rag/_chunker/__init__.py | 10 + .../rag/_chunker/_approx_token_chunker.py | 172 + src/agentscope/rag/_chunker/_base.py | 62 + src/agentscope/rag/_document.py | 102 + src/agentscope/rag/_knowledge.py | 384 + src/agentscope/rag/_parser/__init__.py | 20 + src/agentscope/rag/_parser/_base.py | 116 + src/agentscope/rag/_parser/_excel.py | 462 + src/agentscope/rag/_parser/_image.py | 92 + src/agentscope/rag/_parser/_pdf.py | 92 + src/agentscope/rag/_parser/_ppt.py | 341 + src/agentscope/rag/_parser/_text.py | 125 + src/agentscope/rag/_parser/_utils.py | 99 + src/agentscope/rag/_parser/_word.py | 341 + src/agentscope/rag/_vdb/__init__.py | 22 + src/agentscope/rag/_vdb/_milvus_lite.py | 453 + src/agentscope/rag/_vdb/_mongodb.py | 464 + src/agentscope/rag/_vdb/_qdrant.py | 392 + src/agentscope/rag/_vdb/_vector_store.py | 291 + src/agentscope/skill/__init__.py | 11 + src/agentscope/skill/_base.py | 29 + src/agentscope/skill/_local_loader.py | 171 + src/agentscope/state/__init__.py | 11 + src/agentscope/state/_state.py | 251 + src/agentscope/state/_task.py | 39 + src/agentscope/tool/__init__.py | 58 + src/agentscope/tool/_adapters.py | 394 + src/agentscope/tool/_base.py | 451 + src/agentscope/tool/_builtin/__init__.py | 26 + src/agentscope/tool/_builtin/_backend.py | 747 ++ src/agentscope/tool/_builtin/_bash.py | 778 ++ src/agentscope/tool/_builtin/_bash_parser.py | 884 ++ src/agentscope/tool/_builtin/_edit.py | 416 + src/agentscope/tool/_builtin/_glob.py | 305 + src/agentscope/tool/_builtin/_grep.py | 488 + src/agentscope/tool/_builtin/_meta.py | 130 + src/agentscope/tool/_builtin/_read.py | 285 + .../tool/_builtin/_scripts/__init__.py | 9 + .../tool/_builtin/_scripts/_glob_helper.py | 215 + src/agentscope/tool/_builtin/_skill.py | 127 + src/agentscope/tool/_builtin/_write.py | 328 + src/agentscope/tool/_constants.py | 110 + src/agentscope/tool/_response.py | 164 + src/agentscope/tool/_task/__init__.py | 13 + src/agentscope/tool/_task/_create_task.py | 132 + src/agentscope/tool/_task/_get_task.py | 100 + src/agentscope/tool/_task/_list_task.py | 74 + src/agentscope/tool/_task/_task_tool_base.py | 42 + src/agentscope/tool/_task/_update_task.py | 293 + src/agentscope/tool/_tool_group.py | 108 + src/agentscope/tool/_toolkit.py | 614 ++ src/agentscope/tool/_types.py | 203 + src/agentscope/tool/_utils.py | 160 + src/agentscope/tts/__init__.py | 23 + src/agentscope/tts/_dashscope/__init__.py | 12 + .../tts/_dashscope/_cosyvoice_model.py | 397 + .../_cosyvoice_models/cosyvoice-v3-flash.yaml | 23 + .../_cosyvoice_models/cosyvoice-v3-plus.yaml | 16 + .../tts/_dashscope/_cosyvoice_utils.py | 157 + src/agentscope/tts/_dashscope/_model.py | 252 + .../_models/qwen3-tts-flash-realtime.yaml | 19 + .../_dashscope/_models/qwen3-tts-flash.yaml | 18 + .../tts/_dashscope/_realtime_model.py | 468 + src/agentscope/tts/_openai/__init__.py | 8 + src/agentscope/tts/_openai/_model.py | 252 + .../tts/_openai/_models/gpt-4o-mini-tts.yaml | 30 + .../tts/_openai/_models/tts-1-hd.yaml | 34 + src/agentscope/tts/_openai/_models/tts-1.yaml | 34 + src/agentscope/tts/_tts_base.py | 212 + src/agentscope/tts/_tts_model_card.py | 134 + src/agentscope/tts/_tts_response.py | 56 + src/agentscope/types/__init__.py | 20 + src/agentscope/types/_hook.py | 25 + src/agentscope/types/_json.py | 14 + src/agentscope/types/_object.py | 5 + src/agentscope/workspace/__init__.py | 26 + src/agentscope/workspace/_base.py | 735 ++ .../_docker/Dockerfile.node_copy.template | 4 + .../_docker/Dockerfile.node_from.template | 1 + .../workspace/_docker/Dockerfile.template | 51 + src/agentscope/workspace/_docker/__init__.py | 13 + .../workspace/_docker/_docker_backend.py | 193 + .../workspace/_docker/_docker_workspace.py | 331 + .../workspace/_docker/_make_dockerfile.py | 196 + src/agentscope/workspace/_e2b/__init__.py | 12 + src/agentscope/workspace/_e2b/_constants.py | 28 + src/agentscope/workspace/_e2b/_e2b_backend.py | 163 + .../workspace/_e2b/_e2b_workspace.py | 373 + src/agentscope/workspace/_gateway_client.py | 667 ++ src/agentscope/workspace/_gateway_shim.py | 92 + src/agentscope/workspace/_k8s/__init__.py | 12 + src/agentscope/workspace/_k8s/_constants.py | 42 + src/agentscope/workspace/_k8s/_k8s_backend.py | 358 + .../workspace/_k8s/_k8s_workspace.py | 626 ++ src/agentscope/workspace/_local_workspace.py | 873 ++ .../workspace/_mcp_gateway/__init__.py | 11 + .../workspace/_mcp_gateway/__main__.py | 14 + .../_mcp_gateway/_mcp_gateway_app.py | 190 + src/agentscope/workspace/_offload_protocol.py | 45 + .../workspace/_opensandbox/__init__.py | 12 + .../workspace/_opensandbox/_constants.py | 46 + .../_opensandbox/_opensandbox_backend.py | 277 + .../_opensandbox/_opensandbox_workspace.py | 403 + src/agentscope/workspace/_sandboxed_base.py | 454 + src/agentscope/workspace/_utils.py | 82 + tests/agent_basic_test.py | 1391 +++ tests/agent_interrupt_test.py | 949 ++ tests/agui_protocol_test.py | 716 ++ tests/app_lifespan_dedicated_test.py | 332 + tests/backend_docker_test.py | 214 + tests/backend_e2b_test.py | 149 + tests/backend_local_test.py | 329 + tests/backend_opensandbox_test.py | 155 + tests/blob_store_s3_test.py | 159 + tests/builtin_bash_test.py | 528 + tests/builtin_edit_test.py | 182 + tests/builtin_file_cache_test.py | 432 + tests/builtin_glob_test.py | 236 + tests/builtin_grep_test.py | 254 + tests/builtin_read_test.py | 188 + tests/builtin_write_test.py | 212 + tests/compress_context_test.py | 1083 ++ tests/compress_tool_result_test.py | 480 + tests/docker/k8s_workspace_test.Dockerfile | 67 + tests/embedding_dashscope_test.py | 233 + tests/embedding_gemini_test.py | 179 + tests/embedding_ollama_test.py | 106 + tests/embedding_openai_test.py | 183 + tests/event_test.py | 50 + tests/event_to_message_test.py | 928 ++ tests/formatter_anthropic_test.py | 1167 ++ tests/formatter_dashscope_test.py | 1035 ++ tests/formatter_deepseek_test.py | 532 + tests/formatter_gemini_test.py | 782 ++ tests/formatter_moonshot_test.py | 826 ++ tests/formatter_ollama_test.py | 647 ++ tests/formatter_openai_chat_test.py | 772 ++ tests/formatter_openai_response_test.py | 812 ++ tests/formatter_xai_test.py | 596 ++ tests/hitl_external_execution_test.py | 1359 +++ tests/hitl_mixed_interrupt.py | 1201 +++ tests/hitl_user_confirmation_test.py | 1518 +++ tests/id_factory_test.py | 49 + tests/in_memory_message_bus_test.py | 503 + tests/index_worker_lease_test.py | 196 + tests/mcp_sse_client_test.py | 376 + tests/mcp_streamable_http_client_test.py | 147 + tests/mem0_agentscope_adapter_test.py | 462 + tests/mem0_middleware_test.py | 891 ++ tests/message_test.py | 271 + tests/middleware_background_acting_test.py | 0 tests/middleware_budget_test.py | 476 + tests/middleware_filesystem_memory_test.py | 772 ++ tests/middleware_rag_test.py | 550 + tests/middleware_test.py | 1295 +++ tests/model_anthropic_test.py | 550 + tests/model_base_test.py | 469 + tests/model_count_tokens_test.py | 84 + tests/model_dashscope_test.py | 589 ++ tests/model_deepseek_test.py | 641 ++ tests/model_gemini_test.py | 704 ++ tests/model_moonshot_test.py | 491 + tests/model_ollama_test.py | 365 + tests/model_openai_chat_test.py | 769 ++ tests/model_openai_response_test.py | 570 + tests/model_response_test.py | 456 + tests/model_xai_test.py | 589 ++ tests/permission_bash_parser_test.py | 1317 +++ tests/permission_engine_test.py | 813 ++ tests/permission_mode_test.py | 895 ++ tests/rag_chunker_approx_token_test.py | 204 + tests/rag_parser_test.py | 1194 +++ tests/rag_vdb_milvus_lite_test.py | 358 + tests/rag_vdb_mongodb_test.py | 647 ++ tests/rag_vdb_qdrant_test.py | 376 + tests/reme_middleware_test.py | 1242 +++ tests/resource_access_policy_test.py | 358 + tests/service_agent_interrupt_test.py | 171 + tests/service_cancel_dispatcher_test.py | 463 + tests/service_enqueue_index_task_test.py | 154 + tests/service_inbox_middleware_test.py | 477 + tests/service_index_task_consumer_test.py | 399 + tests/service_knowledge_base_upload_test.py | 444 + tests/service_message_bus_test.py | 591 ++ tests/service_scheduler_test.py | 267 + tests/service_subagent_hitl_projector_test.py | 365 + tests/service_team_tools_test.py | 1697 +++ tests/service_toolkit_test.py | 414 + tests/service_wakeup_dispatcher_test.py | 479 + tests/skill_loader_test.py | 212 + tests/storage_redis_knowledge_base_test.py | 110 + tests/storage_redis_test.py | 1110 ++ tests/task_tool_test.py | 960 ++ tests/test_template.py | 16 + tests/tool_middleware_test.py | 344 + tests/tool_offload_middleware_test.py | 400 + tests/toolkit_skill_test.py | 408 + tests/toolkit_task_test.py | 1187 +++ tests/toolkit_test.py | 1387 +++ tests/tracing_test.py | 846 ++ tests/tts_dashscope_test.py | 1440 +++ tests/tts_middleware_test.py | 426 + tests/tts_openai_test.py | 167 + tests/utils.py | 187 + tests/workspace_docker_test.py | 821 ++ tests/workspace_e2b_test.py | 72 + tests/workspace_k8s_test.py | 420 + tests/workspace_local_test.py | 1300 +++ tests/workspace_opensandbox_test.py | 73 + 755 files changed, 172616 insertions(+) create mode 100644 .gemini/styleguide.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/copilot-instructions.md create mode 100755 .github/scripts/update_news.py create mode 100644 .github/workflows/github-monitor.yml create mode 100644 .github/workflows/k8s-workspace.yml create mode 100644 .github/workflows/pr-title-check.yml create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .github/workflows/publish-pypi.yml create mode 100644 .github/workflows/stale.yml create mode 100644 .github/workflows/unittest.yml create mode 100644 .github/workflows/update_news.yml create mode 100644 .github/workflows/web-ui.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 CONTRIBUTING.md create mode 100644 CONTRIBUTING_zh.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README.wehub.md create mode 100644 README_zh.md create mode 100644 assets/images/agentscope.png create mode 100644 assets/images/bg_tool.gif create mode 100644 assets/images/dingtalk_qr_code.png create mode 100644 assets/images/permission_bypass.gif create mode 100644 assets/images/task.gif create mode 100644 assets/images/team.gif create mode 100644 docs/NEWS.md create mode 100644 docs/NEWS_zh.md create mode 100644 docs/changelog.md create mode 100644 docs/roadmap.md create mode 100644 examples/agent_service/README.md create mode 100644 examples/agent_service/main.py create mode 100644 examples/long_term_memory/agentic_memory/README.md create mode 100644 examples/long_term_memory/agentic_memory/main.py create mode 100644 examples/long_term_memory/mem0/README.md create mode 100644 examples/long_term_memory/mem0/oss_demo.py create mode 100644 examples/long_term_memory/reme/README.md create mode 100644 examples/long_term_memory/reme/reme_demo.py create mode 100644 examples/rag/README.md create mode 100644 examples/rag/index_and_search.py create mode 100644 examples/rag/integrate_with_agent.py create mode 100644 examples/web_ui/.gitignore create mode 100644 examples/web_ui/.husky/pre-commit create mode 100644 examples/web_ui/.prettierignore create mode 100644 examples/web_ui/.prettierrc create mode 100644 examples/web_ui/backend/package.json create mode 100644 examples/web_ui/backend/src/index.ts create mode 100644 examples/web_ui/backend/tsconfig.json create mode 100644 examples/web_ui/frontend/README.md create mode 100644 examples/web_ui/frontend/components.json create mode 100644 examples/web_ui/frontend/eslint.config.js create mode 100644 examples/web_ui/frontend/index.html create mode 100644 examples/web_ui/frontend/package.json create mode 100644 examples/web_ui/frontend/public/agentscope.svg create mode 100644 examples/web_ui/frontend/src/App.tsx create mode 100644 examples/web_ui/frontend/src/api/agent.ts create mode 100644 examples/web_ui/frontend/src/api/chat.ts create mode 100644 examples/web_ui/frontend/src/api/client.ts create mode 100644 examples/web_ui/frontend/src/api/credential.ts create mode 100644 examples/web_ui/frontend/src/api/index.ts create mode 100644 examples/web_ui/frontend/src/api/knowledgeBase.ts create mode 100644 examples/web_ui/frontend/src/api/model.ts create mode 100644 examples/web_ui/frontend/src/api/schedule.ts create mode 100644 examples/web_ui/frontend/src/api/session.ts create mode 100644 examples/web_ui/frontend/src/api/types.ts create mode 100644 examples/web_ui/frontend/src/api/workspace.ts create mode 100644 examples/web_ui/frontend/src/assets/images/agentscope.svg create mode 100644 examples/web_ui/frontend/src/assets/images/line-corner.svg create mode 100644 examples/web_ui/frontend/src/assets/images/line-vertical.svg create mode 100644 examples/web_ui/frontend/src/assets/images/mcp.svg create mode 100644 examples/web_ui/frontend/src/components/badge/InputTypeBadges.tsx create mode 100644 examples/web_ui/frontend/src/components/badge/StatusBadge.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/ChatContent.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/ConfirmCard.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/Empty.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/FileAttachment.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/MessageBubble.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/SubagentHitlCard.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/TextInput.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/BashRenderer.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/DefaultRenderer.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/DiffPreview.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/EditRenderer.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/GlobRenderer.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/GrepRenderer.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/ReadRenderer.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/TaskCreateRenderer.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/WriteRenderer.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/_shared.tsx create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/index.ts create mode 100644 examples/web_ui/frontend/src/components/chat/tool-renderers/types.ts create mode 100644 examples/web_ui/frontend/src/components/dialog/AddSkillDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/dialog/AgentDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/dialog/CreateCredentialDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/dialog/CreateKnowledgeBaseDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/dialog/DeleteDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/dialog/EditAgentDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/dialog/EditCredentialDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/dialog/EditKnowledgeBaseDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/dialog/MCPDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/dialog/RenameSessionDialog.tsx create mode 100644 examples/web_ui/frontend/src/components/drawer/KnowledgeSearchDrawer.tsx create mode 100644 examples/web_ui/frontend/src/components/error/RouteError.tsx create mode 100644 examples/web_ui/frontend/src/components/form/AgentFormFields.tsx create mode 100644 examples/web_ui/frontend/src/components/form/SchemaForm.tsx create mode 100644 examples/web_ui/frontend/src/components/knowledge/KnowledgeDocumentsPanel.tsx create mode 100644 examples/web_ui/frontend/src/components/layout/AppLayout.tsx create mode 100644 examples/web_ui/frontend/src/components/layout/AppSidebar.tsx create mode 100644 examples/web_ui/frontend/src/components/panel/KnowledgeBasePanel.tsx create mode 100644 examples/web_ui/frontend/src/components/panel/McpPanel.tsx create mode 100644 examples/web_ui/frontend/src/components/panel/PanelDock.tsx create mode 100644 examples/web_ui/frontend/src/components/panel/PanelEmpty.tsx create mode 100644 examples/web_ui/frontend/src/components/panel/PermissionPanel.tsx create mode 100644 examples/web_ui/frontend/src/components/panel/SkillPanel.tsx create mode 100644 examples/web_ui/frontend/src/components/panel/TaskPanel.tsx create mode 100644 examples/web_ui/frontend/src/components/popover/KnowledgeBaseParametersPopover.tsx create mode 100644 examples/web_ui/frontend/src/components/popover/ModelParametersPopover.tsx create mode 100644 examples/web_ui/frontend/src/components/select/AgentSelect.tsx create mode 100644 examples/web_ui/frontend/src/components/select/DimensionSelect.tsx create mode 100644 examples/web_ui/frontend/src/components/select/EmbeddingSelect.tsx create mode 100644 examples/web_ui/frontend/src/components/select/LlmSelect.tsx create mode 100644 examples/web_ui/frontend/src/components/select/PermissionModeSelect.tsx create mode 100644 examples/web_ui/frontend/src/components/select/TimezoneSelect.tsx create mode 100644 examples/web_ui/frontend/src/components/team/TeamSidebar.tsx create mode 100644 examples/web_ui/frontend/src/components/tour/ChatTourController.tsx create mode 100644 examples/web_ui/frontend/src/components/tour/TourCard.tsx create mode 100644 examples/web_ui/frontend/src/components/tour/chatTourSteps.ts create mode 100644 examples/web_ui/frontend/src/components/ui/alert.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/badge.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/button-group.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/button.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/calendar.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/card.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/checkbox.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/collapsible.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/dialog.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/drawer.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/dropdown-menu.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/empty.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/field.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/input-group.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/input.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/item.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/kbd.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/label.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/popover.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/resizable.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/select.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/separator.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/sheet.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/sidebar.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/skeleton.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/sonner.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/spinner.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/switch.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/tabs.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/textarea.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/toggle.tsx create mode 100644 examples/web_ui/frontend/src/components/ui/tooltip.tsx create mode 100644 examples/web_ui/frontend/src/context/AudioContext.tsx create mode 100644 examples/web_ui/frontend/src/context/UploadContext.tsx create mode 100644 examples/web_ui/frontend/src/context/uploadTypes.ts create mode 100644 examples/web_ui/frontend/src/hooks/use-mobile.ts create mode 100644 examples/web_ui/frontend/src/hooks/useAgentSchema.ts create mode 100644 examples/web_ui/frontend/src/hooks/useAgents.ts create mode 100644 examples/web_ui/frontend/src/hooks/useAvailableModels.ts create mode 100644 examples/web_ui/frontend/src/hooks/useAvailableTTSModels.ts create mode 100644 examples/web_ui/frontend/src/hooks/useChat.ts create mode 100644 examples/web_ui/frontend/src/hooks/useCredentials.ts create mode 100644 examples/web_ui/frontend/src/hooks/useDocumentStatusPolling.ts create mode 100644 examples/web_ui/frontend/src/hooks/useKbEmbeddingModels.ts create mode 100644 examples/web_ui/frontend/src/hooks/useKnowledgeBaseMiddlewareSchema.ts create mode 100644 examples/web_ui/frontend/src/hooks/useKnowledgeBases.ts create mode 100644 examples/web_ui/frontend/src/hooks/useKnowledgeDocuments.ts create mode 100644 examples/web_ui/frontend/src/hooks/useKnowledgeSupportedContentTypes.ts create mode 100644 examples/web_ui/frontend/src/hooks/useMessages.ts create mode 100644 examples/web_ui/frontend/src/hooks/useModels.ts create mode 100644 examples/web_ui/frontend/src/hooks/useSchedules.ts create mode 100644 examples/web_ui/frontend/src/hooks/useSessions.ts create mode 100644 examples/web_ui/frontend/src/hooks/useSkills.ts create mode 100644 examples/web_ui/frontend/src/hooks/useWorkspace.ts create mode 100644 examples/web_ui/frontend/src/i18n/index.ts create mode 100644 examples/web_ui/frontend/src/i18n/locales/en.json create mode 100644 examples/web_ui/frontend/src/i18n/locales/zh.json create mode 100644 examples/web_ui/frontend/src/i18n/useI18n.ts create mode 100644 examples/web_ui/frontend/src/index.css create mode 100644 examples/web_ui/frontend/src/lib/api-error.ts create mode 100644 examples/web_ui/frontend/src/lib/next-navigation-shim.ts create mode 100644 examples/web_ui/frontend/src/lib/utils.ts create mode 100644 examples/web_ui/frontend/src/main.tsx create mode 100644 examples/web_ui/frontend/src/pages/chat/ChatViewport.tsx create mode 100644 examples/web_ui/frontend/src/pages/chat/index.tsx create mode 100644 examples/web_ui/frontend/src/pages/credential/index.tsx create mode 100644 examples/web_ui/frontend/src/pages/knowledge/index.tsx create mode 100644 examples/web_ui/frontend/src/pages/schedule/calendar-tab-page.tsx create mode 100644 examples/web_ui/frontend/src/pages/schedule/create-schedule-dialog.tsx create mode 100644 examples/web_ui/frontend/src/pages/schedule/empty-state.tsx create mode 100644 examples/web_ui/frontend/src/pages/schedule/event.tsx create mode 100644 examples/web_ui/frontend/src/pages/schedule/index.tsx create mode 100644 examples/web_ui/frontend/src/pages/schedule/list-tab-page.tsx create mode 100644 examples/web_ui/frontend/src/pages/schedule/schedule-card.tsx create mode 100644 examples/web_ui/frontend/src/pages/schedule/schedule-detail-drawer.tsx create mode 100644 examples/web_ui/frontend/src/pages/schedule/schedule-utils.ts create mode 100644 examples/web_ui/frontend/src/pages/setup/index.tsx create mode 100644 examples/web_ui/frontend/src/types/unidiff.d.ts create mode 100644 examples/web_ui/frontend/src/utils/common.ts create mode 100644 examples/web_ui/frontend/src/utils/platform.ts create mode 100644 examples/web_ui/frontend/src/utils/streamingAudio.ts create mode 100644 examples/web_ui/frontend/src/vite-env.d.ts create mode 100644 examples/web_ui/frontend/tsconfig.app.json create mode 100644 examples/web_ui/frontend/tsconfig.json create mode 100644 examples/web_ui/frontend/tsconfig.node.json create mode 100644 examples/web_ui/frontend/vite.config.ts create mode 100644 examples/web_ui/package.json create mode 100644 examples/web_ui/pnpm-lock.yaml create mode 100644 examples/web_ui/pnpm-workspace.yaml create mode 100644 pyproject.toml create mode 100644 scripts/model_examples/README.md create mode 100644 scripts/model_examples/_utils.py create mode 100644 scripts/model_examples/anthropic_call.py create mode 100644 scripts/model_examples/anthropic_multiagent.py create mode 100644 scripts/model_examples/anthropic_multiagent_multimodal.py create mode 100644 scripts/model_examples/anthropic_multimodal.py create mode 100644 scripts/model_examples/dashscope_call.py create mode 100644 scripts/model_examples/dashscope_multiagent.py create mode 100644 scripts/model_examples/dashscope_multiagent_multimodal.py create mode 100644 scripts/model_examples/dashscope_multimodal.py create mode 100644 scripts/model_examples/deepseek_call.py create mode 100644 scripts/model_examples/deepseek_multiagent.py create mode 100644 scripts/model_examples/gemini_call.py create mode 100644 scripts/model_examples/gemini_multiagent.py create mode 100644 scripts/model_examples/gemini_multiagent_multimodal.py create mode 100644 scripts/model_examples/gemini_multimodal.py create mode 100644 scripts/model_examples/moonshot_call.py create mode 100644 scripts/model_examples/moonshot_multiagent.py create mode 100644 scripts/model_examples/moonshot_multiagent_multimodal.py create mode 100644 scripts/model_examples/moonshot_multimodal.py create mode 100644 scripts/model_examples/ollama_call.py create mode 100644 scripts/model_examples/ollama_multiagent.py create mode 100644 scripts/model_examples/ollama_multiagent_multimodal.py create mode 100644 scripts/model_examples/ollama_multimodal.py create mode 100644 scripts/model_examples/openai_chat_call.py create mode 100644 scripts/model_examples/openai_chat_multiagent.py create mode 100644 scripts/model_examples/openai_chat_multiagent_multimodal.py create mode 100644 scripts/model_examples/openai_chat_multimodal.py create mode 100644 scripts/model_examples/openai_response_call.py create mode 100644 scripts/model_examples/openai_response_multiagent.py create mode 100644 scripts/model_examples/openai_response_multiagent_multimodal.py create mode 100644 scripts/model_examples/openai_response_multimodal.py create mode 100644 scripts/model_examples/run_tests.py create mode 100644 scripts/model_examples/test.jpeg create mode 100644 scripts/model_examples/xai_call.py create mode 100644 scripts/model_examples/xai_multiagent.py create mode 100644 scripts/model_examples/xai_multiagent_multimodal.py create mode 100644 scripts/model_examples/xai_multimodal.py create mode 100644 src/agentscope/__init__.py create mode 100644 src/agentscope/_logging.py create mode 100644 src/agentscope/_utils/__init__.py create mode 100644 src/agentscope/_utils/_audio.py create mode 100644 src/agentscope/_utils/_common.py create mode 100644 src/agentscope/_utils/_mixin.py create mode 100644 src/agentscope/_version.py create mode 100644 src/agentscope/agent/__init__.py create mode 100644 src/agentscope/agent/_agent.py create mode 100644 src/agentscope/agent/_config.py create mode 100644 src/agentscope/agent/_utils.py create mode 100644 src/agentscope/app/__init__.py create mode 100644 src/agentscope/app/_app.py create mode 100644 src/agentscope/app/_bus_ops.py create mode 100644 src/agentscope/app/_lifespan.py create mode 100644 src/agentscope/app/_manager/__init__.py create mode 100644 src/agentscope/app/_manager/_background_task_manager.py create mode 100644 src/agentscope/app/_manager/_cancel_dispatcher.py create mode 100644 src/agentscope/app/_manager/_chat_run_registry.py create mode 100644 src/agentscope/app/_manager/_scheduler/__init__.py create mode 100644 src/agentscope/app/_manager/_scheduler/_scheduler_manager.py create mode 100644 src/agentscope/app/_manager/_scheduler/_tools/__init__.py create mode 100644 src/agentscope/app/_manager/_scheduler/_tools/_schedule_create.py create mode 100644 src/agentscope/app/_manager/_scheduler/_tools/_schedule_delete.py create mode 100644 src/agentscope/app/_manager/_scheduler/_tools/_schedule_list.py create mode 100644 src/agentscope/app/_manager/_scheduler/_tools/_schedule_view.py create mode 100644 src/agentscope/app/_manager/_wakeup_dispatcher.py create mode 100644 src/agentscope/app/_router/__init__.py create mode 100644 src/agentscope/app/_router/_agent.py create mode 100644 src/agentscope/app/_router/_chat.py create mode 100644 src/agentscope/app/_router/_credential.py create mode 100644 src/agentscope/app/_router/_knowledge_base.py create mode 100644 src/agentscope/app/_router/_model.py create mode 100644 src/agentscope/app/_router/_schedule.py create mode 100644 src/agentscope/app/_router/_schema/__init__.py create mode 100644 src/agentscope/app/_router/_schema/_agent.py create mode 100644 src/agentscope/app/_router/_schema/_chat.py create mode 100644 src/agentscope/app/_router/_schema/_credential.py create mode 100644 src/agentscope/app/_router/_schema/_knowledge_base.py create mode 100644 src/agentscope/app/_router/_schema/_mcp.py create mode 100644 src/agentscope/app/_router/_schema/_model.py create mode 100644 src/agentscope/app/_router/_schema/_schedule.py create mode 100644 src/agentscope/app/_router/_schema/_session.py create mode 100644 src/agentscope/app/_router/_schema/_tts_model.py create mode 100644 src/agentscope/app/_router/_session.py create mode 100644 src/agentscope/app/_router/_tts_model.py create mode 100644 src/agentscope/app/_router/_workspace.py create mode 100644 src/agentscope/app/_service/__init__.py create mode 100644 src/agentscope/app/_service/_access.py create mode 100644 src/agentscope/app/_service/_chat.py create mode 100644 src/agentscope/app/_service/_embedding.py create mode 100644 src/agentscope/app/_service/_index_sweeper.py create mode 100644 src/agentscope/app/_service/_index_task_consumer.py create mode 100644 src/agentscope/app/_service/_index_worker.py create mode 100644 src/agentscope/app/_service/_knowledge_base.py create mode 100644 src/agentscope/app/_service/_model.py create mode 100644 src/agentscope/app/_service/_projectors/__init__.py create mode 100644 src/agentscope/app/_service/_projectors/_subagent_hitl.py create mode 100644 src/agentscope/app/_service/_session.py create mode 100644 src/agentscope/app/_service/_session_projection.py create mode 100644 src/agentscope/app/_service/_toolkit.py create mode 100644 src/agentscope/app/_service/_tts_model.py create mode 100644 src/agentscope/app/_tool/__init__.py create mode 100644 src/agentscope/app/_tool/_agent_create.py create mode 100644 src/agentscope/app/_tool/_agent_invite.py create mode 100644 src/agentscope/app/_tool/_constants.py create mode 100644 src/agentscope/app/_tool/_team_create.py create mode 100644 src/agentscope/app/_tool/_team_delete.py create mode 100644 src/agentscope/app/_tool/_team_say.py create mode 100644 src/agentscope/app/_tool/_team_tool_base.py create mode 100644 src/agentscope/app/_types.py create mode 100644 src/agentscope/app/access/__init__.py create mode 100644 src/agentscope/app/access/_policy.py create mode 100644 src/agentscope/app/deps.py create mode 100644 src/agentscope/app/message_bus/__init__.py create mode 100644 src/agentscope/app/message_bus/_base.py create mode 100644 src/agentscope/app/message_bus/_in_memory_message_bus.py create mode 100644 src/agentscope/app/message_bus/_keys.py create mode 100644 src/agentscope/app/message_bus/_redis_message_bus.py create mode 100644 src/agentscope/app/middleware/__init__.py create mode 100644 src/agentscope/app/middleware/_inbox_middleware.py create mode 100644 src/agentscope/app/middleware/_protocol/__init__.py create mode 100644 src/agentscope/app/middleware/_protocol/_agui.py create mode 100644 src/agentscope/app/middleware/_protocol/_base.py create mode 100644 src/agentscope/app/middleware/_state_change_middleware.py create mode 100644 src/agentscope/app/middleware/_tool_offload_middleware.py create mode 100644 src/agentscope/app/rag/__init__.py create mode 100644 src/agentscope/app/rag/blob_store/__init__.py create mode 100644 src/agentscope/app/rag/blob_store/_base.py create mode 100644 src/agentscope/app/rag/blob_store/_local.py create mode 100644 src/agentscope/app/rag/blob_store/_s3.py create mode 100644 src/agentscope/app/rag/index_worker/__init__.py create mode 100644 src/agentscope/app/rag/index_worker/__main__.py create mode 100644 src/agentscope/app/rag/knowledge_base_manager/__init__.py create mode 100644 src/agentscope/app/rag/knowledge_base_manager/_base.py create mode 100644 src/agentscope/app/rag/knowledge_base_manager/_collection_per_kb.py create mode 100644 src/agentscope/app/rag/knowledge_base_manager/_dimension_policy.py create mode 100644 src/agentscope/app/rag/knowledge_base_manager/_errors.py create mode 100644 src/agentscope/app/storage/__init__.py create mode 100644 src/agentscope/app/storage/_base.py create mode 100644 src/agentscope/app/storage/_model/__init__.py create mode 100644 src/agentscope/app/storage/_model/_agent.py create mode 100644 src/agentscope/app/storage/_model/_base.py create mode 100644 src/agentscope/app/storage/_model/_credential.py create mode 100644 src/agentscope/app/storage/_model/_knowledge_base.py create mode 100644 src/agentscope/app/storage/_model/_knowledge_document.py create mode 100644 src/agentscope/app/storage/_model/_schedule.py create mode 100644 src/agentscope/app/storage/_model/_session.py create mode 100644 src/agentscope/app/storage/_model/_team.py create mode 100644 src/agentscope/app/storage/_model/_user.py create mode 100644 src/agentscope/app/storage/_redis_storage.py create mode 100644 src/agentscope/app/storage/_utils.py create mode 100644 src/agentscope/app/workspace_manager/__init__.py create mode 100644 src/agentscope/app/workspace_manager/_base.py create mode 100644 src/agentscope/app/workspace_manager/_docker_workspace_manager.py create mode 100644 src/agentscope/app/workspace_manager/_e2b_workspace_manager.py create mode 100644 src/agentscope/app/workspace_manager/_k8s_workspace_manager.py create mode 100644 src/agentscope/app/workspace_manager/_local_workspace_manager.py create mode 100644 src/agentscope/app/workspace_manager/_opensandbox_workspace_manager.py create mode 100644 src/agentscope/credential/__init__.py create mode 100644 src/agentscope/credential/_anthropic.py create mode 100644 src/agentscope/credential/_base.py create mode 100644 src/agentscope/credential/_dashscope.py create mode 100644 src/agentscope/credential/_deepseek.py create mode 100644 src/agentscope/credential/_factory.py create mode 100644 src/agentscope/credential/_gemini.py create mode 100644 src/agentscope/credential/_kimi.py create mode 100644 src/agentscope/credential/_moonshot.py create mode 100644 src/agentscope/credential/_ollama.py create mode 100644 src/agentscope/credential/_openai.py create mode 100644 src/agentscope/credential/_xai.py create mode 100644 src/agentscope/embedding/__init__.py create mode 100644 src/agentscope/embedding/_cache_base.py create mode 100644 src/agentscope/embedding/_dashscope/__init__.py create mode 100644 src/agentscope/embedding/_dashscope/_model.py create mode 100644 src/agentscope/embedding/_dashscope/_models/multimodal-embedding-v1.yaml create mode 100644 src/agentscope/embedding/_dashscope/_models/qwen2.5-vl-embedding.yaml create mode 100644 src/agentscope/embedding/_dashscope/_models/qwen3-vl-embedding.yaml create mode 100644 src/agentscope/embedding/_dashscope/_models/text-embedding-v3.yaml create mode 100644 src/agentscope/embedding/_dashscope/_models/text-embedding-v4.yaml create mode 100644 src/agentscope/embedding/_dashscope/_models/tongyi-embedding-vision-flash.yaml create mode 100644 src/agentscope/embedding/_dashscope/_models/tongyi-embedding-vision-plus.yaml create mode 100644 src/agentscope/embedding/_embedding_base.py create mode 100644 src/agentscope/embedding/_embedding_model_card.py create mode 100644 src/agentscope/embedding/_embedding_response.py create mode 100644 src/agentscope/embedding/_embedding_usage.py create mode 100644 src/agentscope/embedding/_file_cache.py create mode 100644 src/agentscope/embedding/_gemini/__init__.py create mode 100644 src/agentscope/embedding/_gemini/_model.py create mode 100644 src/agentscope/embedding/_gemini/_models/gemini-embedding-001.yaml create mode 100644 src/agentscope/embedding/_gemini/_models/gemini-embedding-2.yaml create mode 100644 src/agentscope/embedding/_ollama/__init__.py create mode 100644 src/agentscope/embedding/_ollama/_model.py create mode 100644 src/agentscope/embedding/_openai/__init__.py create mode 100644 src/agentscope/embedding/_openai/_model.py create mode 100644 src/agentscope/embedding/_openai/_models/text-embedding-3-large.yaml create mode 100644 src/agentscope/embedding/_openai/_models/text-embedding-3-small.yaml create mode 100644 src/agentscope/event/__init__.py create mode 100644 src/agentscope/event/_event.py create mode 100644 src/agentscope/exception/__init__.py create mode 100644 src/agentscope/exception/_base.py create mode 100644 src/agentscope/exception/_tool.py create mode 100644 src/agentscope/formatter/__init__.py create mode 100644 src/agentscope/formatter/_anthropic_formatter.py create mode 100644 src/agentscope/formatter/_dashscope_formatter.py create mode 100644 src/agentscope/formatter/_deepseek_formatter.py create mode 100644 src/agentscope/formatter/_formatter_base.py create mode 100644 src/agentscope/formatter/_gemini_formatter.py create mode 100644 src/agentscope/formatter/_moonshot_formatter.py create mode 100644 src/agentscope/formatter/_ollama_formatter.py create mode 100644 src/agentscope/formatter/_openai_formatter.py create mode 100644 src/agentscope/formatter/_openai_response_formatter.py create mode 100644 src/agentscope/formatter/_xai_formatter.py create mode 100644 src/agentscope/mcp/__init__.py create mode 100644 src/agentscope/mcp/_config.py create mode 100644 src/agentscope/mcp/_mcp_client.py create mode 100644 src/agentscope/message/__init__.py create mode 100644 src/agentscope/message/_base.py create mode 100644 src/agentscope/message/_block.py create mode 100644 src/agentscope/middleware/__init__.py create mode 100644 src/agentscope/middleware/_base.py create mode 100644 src/agentscope/middleware/_budget.py create mode 100644 src/agentscope/middleware/_longterm_memory/__init__.py create mode 100644 src/agentscope/middleware/_longterm_memory/_agentic_memory/__init__.py create mode 100644 src/agentscope/middleware/_longterm_memory/_agentic_memory/_middleware.py create mode 100644 src/agentscope/middleware/_longterm_memory/_mem0/__init__.py create mode 100644 src/agentscope/middleware/_longterm_memory/_mem0/_agentscope_adapter.py create mode 100644 src/agentscope/middleware/_longterm_memory/_mem0/_middleware.py create mode 100644 src/agentscope/middleware/_longterm_memory/_mem0/_tools.py create mode 100644 src/agentscope/middleware/_longterm_memory/_mem0/_utils.py create mode 100644 src/agentscope/middleware/_longterm_memory/_reme/__init__.py create mode 100644 src/agentscope/middleware/_longterm_memory/_reme/_middleware.py create mode 100644 src/agentscope/middleware/_longterm_memory/_reme/_tools.py create mode 100644 src/agentscope/middleware/_longterm_memory/_reme/_utils.py create mode 100644 src/agentscope/middleware/_rag.py create mode 100644 src/agentscope/middleware/_tracing/__init__.py create mode 100644 src/agentscope/middleware/_tracing/_attributes.py create mode 100644 src/agentscope/middleware/_tracing/_converter.py create mode 100644 src/agentscope/middleware/_tracing/_extractor.py create mode 100644 src/agentscope/middleware/_tracing/_setup.py create mode 100644 src/agentscope/middleware/_tracing/_trace.py create mode 100644 src/agentscope/middleware/_tracing/_utils.py create mode 100644 src/agentscope/middleware/_tts_middleware.py create mode 100644 src/agentscope/model/__init__.py create mode 100644 src/agentscope/model/_anthropic/__init__.py create mode 100644 src/agentscope/model/_anthropic/_model.py create mode 100644 src/agentscope/model/_anthropic/_models/claude-haiku-4-5.yaml create mode 100644 src/agentscope/model/_anthropic/_models/claude-opus-4-5.yaml create mode 100644 src/agentscope/model/_anthropic/_models/claude-opus-4-6.yaml create mode 100644 src/agentscope/model/_anthropic/_models/claude-opus-4-7.yaml create mode 100644 src/agentscope/model/_anthropic/_models/claude-opus-4-8.yaml create mode 100644 src/agentscope/model/_anthropic/_models/claude-sonnet-4-5.yaml create mode 100644 src/agentscope/model/_anthropic/_models/claude-sonnet-4-6.yaml create mode 100644 src/agentscope/model/_base.py create mode 100644 src/agentscope/model/_dashscope/__init__.py create mode 100644 src/agentscope/model/_dashscope/_model.py create mode 100644 src/agentscope/model/_dashscope/_models/qwen-long.yaml create mode 100644 src/agentscope/model/_dashscope/_models/qwen-plus.yaml create mode 100644 src/agentscope/model/_dashscope/_models/qwen3.5-omni-plus.yaml create mode 100644 src/agentscope/model/_dashscope/_models/qwen3.5-plus.yaml create mode 100644 src/agentscope/model/_dashscope/_models/qwen3.6-max-preview.yaml create mode 100644 src/agentscope/model/_dashscope/_models/qwen3.6-plus.yaml create mode 100644 src/agentscope/model/_dashscope/_models/qwen3.7-max.yaml create mode 100644 src/agentscope/model/_deepseek/__init__.py create mode 100644 src/agentscope/model/_deepseek/_model.py create mode 100644 src/agentscope/model/_deepseek/_models/deepseek-chat.yaml create mode 100644 src/agentscope/model/_deepseek/_models/deepseek-reasoner.yaml create mode 100644 src/agentscope/model/_deepseek/_models/deepseek-v4-flash.yaml create mode 100644 src/agentscope/model/_deepseek/_models/deepseek-v4-pro.yaml create mode 100644 src/agentscope/model/_gemini/__init__.py create mode 100644 src/agentscope/model/_gemini/_model.py create mode 100644 src/agentscope/model/_gemini/_models/gemini-2.5-flash.yaml create mode 100644 src/agentscope/model/_gemini/_models/gemini-2.5-pro.yaml create mode 100644 src/agentscope/model/_gemini/_models/gemini-3-flash-preview.yaml create mode 100644 src/agentscope/model/_gemini/_models/gemini-3.1-pro-preview.yaml create mode 100644 src/agentscope/model/_model_card.py create mode 100644 src/agentscope/model/_model_response.py create mode 100644 src/agentscope/model/_model_usage.py create mode 100644 src/agentscope/model/_moonshot/__init__.py create mode 100644 src/agentscope/model/_moonshot/_model.py create mode 100644 src/agentscope/model/_moonshot/_models/kimi-k2.5.yaml create mode 100644 src/agentscope/model/_moonshot/_models/kimi-k2.6.yaml create mode 100644 src/agentscope/model/_moonshot/_models/moonshot-v1-128k.yaml create mode 100644 src/agentscope/model/_moonshot/_models/moonshot-v1-32k.yaml create mode 100644 src/agentscope/model/_moonshot/_models/moonshot-v1-8k.yaml create mode 100644 src/agentscope/model/_ollama/__init__.py create mode 100644 src/agentscope/model/_ollama/_model.py create mode 100644 src/agentscope/model/_ollama/_models/deepseek-r1-14b.yaml create mode 100644 src/agentscope/model/_ollama/_models/llama4.yaml create mode 100644 src/agentscope/model/_ollama/_models/qwen3-14b.yaml create mode 100644 src/agentscope/model/_ollama/_models/qwen3.5-9b.yaml create mode 100644 src/agentscope/model/_openai_chat/__init__.py create mode 100644 src/agentscope/model/_openai_chat/_model.py create mode 100644 src/agentscope/model/_openai_chat/_models/gpt-4.1-mini.yaml create mode 100644 src/agentscope/model/_openai_chat/_models/gpt-4.1-nano.yaml create mode 100644 src/agentscope/model/_openai_chat/_models/gpt-4.1.yaml create mode 100644 src/agentscope/model/_openai_chat/_models/gpt-4o-mini.yaml create mode 100644 src/agentscope/model/_openai_chat/_models/gpt-4o.yaml create mode 100644 src/agentscope/model/_openai_chat/_models/gpt-5.4.yaml create mode 100644 src/agentscope/model/_openai_chat/_models/gpt-5.5.yaml create mode 100644 src/agentscope/model/_openai_chat/_models/gpt-audio-mini.yaml create mode 100644 src/agentscope/model/_openai_chat/_models/o3.yaml create mode 100644 src/agentscope/model/_openai_chat/_models/o4-mini.yaml create mode 100644 src/agentscope/model/_openai_response/__init__.py create mode 100644 src/agentscope/model/_openai_response/_model.py create mode 100644 src/agentscope/model/_openai_response/_models/gpt-4.1-mini.yaml create mode 100644 src/agentscope/model/_openai_response/_models/gpt-4.1-nano.yaml create mode 100644 src/agentscope/model/_openai_response/_models/gpt-4.1.yaml create mode 100644 src/agentscope/model/_openai_response/_models/gpt-4o-mini.yaml create mode 100644 src/agentscope/model/_openai_response/_models/gpt-4o.yaml create mode 100644 src/agentscope/model/_openai_response/_models/gpt-5.4.yaml create mode 100644 src/agentscope/model/_openai_response/_models/gpt-5.5.yaml create mode 100644 src/agentscope/model/_openai_response/_models/o3.yaml create mode 100644 src/agentscope/model/_openai_response/_models/o4-mini.yaml create mode 100644 src/agentscope/model/_xai/__init__.py create mode 100644 src/agentscope/model/_xai/_model.py create mode 100644 src/agentscope/model/_xai/_models/grok-3-fast.yaml create mode 100644 src/agentscope/model/_xai/_models/grok-3-mini.yaml create mode 100644 src/agentscope/model/_xai/_models/grok-3.yaml create mode 100644 src/agentscope/model/_xai/_models/grok-4.3.yaml create mode 100644 src/agentscope/permission/__init__.py create mode 100644 src/agentscope/permission/_context.py create mode 100644 src/agentscope/permission/_decision.py create mode 100644 src/agentscope/permission/_engine.py create mode 100644 src/agentscope/permission/_rule.py create mode 100644 src/agentscope/permission/_types.py create mode 100644 src/agentscope/py.typed create mode 100644 src/agentscope/rag/__init__.py create mode 100644 src/agentscope/rag/_chunker/__init__.py create mode 100644 src/agentscope/rag/_chunker/_approx_token_chunker.py create mode 100644 src/agentscope/rag/_chunker/_base.py create mode 100644 src/agentscope/rag/_document.py create mode 100644 src/agentscope/rag/_knowledge.py create mode 100644 src/agentscope/rag/_parser/__init__.py create mode 100644 src/agentscope/rag/_parser/_base.py create mode 100644 src/agentscope/rag/_parser/_excel.py create mode 100644 src/agentscope/rag/_parser/_image.py create mode 100644 src/agentscope/rag/_parser/_pdf.py create mode 100644 src/agentscope/rag/_parser/_ppt.py create mode 100644 src/agentscope/rag/_parser/_text.py create mode 100644 src/agentscope/rag/_parser/_utils.py create mode 100644 src/agentscope/rag/_parser/_word.py create mode 100644 src/agentscope/rag/_vdb/__init__.py create mode 100644 src/agentscope/rag/_vdb/_milvus_lite.py create mode 100644 src/agentscope/rag/_vdb/_mongodb.py create mode 100644 src/agentscope/rag/_vdb/_qdrant.py create mode 100644 src/agentscope/rag/_vdb/_vector_store.py create mode 100644 src/agentscope/skill/__init__.py create mode 100644 src/agentscope/skill/_base.py create mode 100644 src/agentscope/skill/_local_loader.py create mode 100644 src/agentscope/state/__init__.py create mode 100644 src/agentscope/state/_state.py create mode 100644 src/agentscope/state/_task.py create mode 100644 src/agentscope/tool/__init__.py create mode 100644 src/agentscope/tool/_adapters.py create mode 100644 src/agentscope/tool/_base.py create mode 100644 src/agentscope/tool/_builtin/__init__.py create mode 100644 src/agentscope/tool/_builtin/_backend.py create mode 100644 src/agentscope/tool/_builtin/_bash.py create mode 100644 src/agentscope/tool/_builtin/_bash_parser.py create mode 100644 src/agentscope/tool/_builtin/_edit.py create mode 100644 src/agentscope/tool/_builtin/_glob.py create mode 100644 src/agentscope/tool/_builtin/_grep.py create mode 100644 src/agentscope/tool/_builtin/_meta.py create mode 100644 src/agentscope/tool/_builtin/_read.py create mode 100644 src/agentscope/tool/_builtin/_scripts/__init__.py create mode 100644 src/agentscope/tool/_builtin/_scripts/_glob_helper.py create mode 100644 src/agentscope/tool/_builtin/_skill.py create mode 100644 src/agentscope/tool/_builtin/_write.py create mode 100644 src/agentscope/tool/_constants.py create mode 100644 src/agentscope/tool/_response.py create mode 100644 src/agentscope/tool/_task/__init__.py create mode 100644 src/agentscope/tool/_task/_create_task.py create mode 100644 src/agentscope/tool/_task/_get_task.py create mode 100644 src/agentscope/tool/_task/_list_task.py create mode 100644 src/agentscope/tool/_task/_task_tool_base.py create mode 100644 src/agentscope/tool/_task/_update_task.py create mode 100644 src/agentscope/tool/_tool_group.py create mode 100644 src/agentscope/tool/_toolkit.py create mode 100644 src/agentscope/tool/_types.py create mode 100644 src/agentscope/tool/_utils.py create mode 100644 src/agentscope/tts/__init__.py create mode 100644 src/agentscope/tts/_dashscope/__init__.py create mode 100644 src/agentscope/tts/_dashscope/_cosyvoice_model.py create mode 100644 src/agentscope/tts/_dashscope/_cosyvoice_models/cosyvoice-v3-flash.yaml create mode 100644 src/agentscope/tts/_dashscope/_cosyvoice_models/cosyvoice-v3-plus.yaml create mode 100644 src/agentscope/tts/_dashscope/_cosyvoice_utils.py create mode 100644 src/agentscope/tts/_dashscope/_model.py create mode 100644 src/agentscope/tts/_dashscope/_models/qwen3-tts-flash-realtime.yaml create mode 100644 src/agentscope/tts/_dashscope/_models/qwen3-tts-flash.yaml create mode 100644 src/agentscope/tts/_dashscope/_realtime_model.py create mode 100644 src/agentscope/tts/_openai/__init__.py create mode 100644 src/agentscope/tts/_openai/_model.py create mode 100644 src/agentscope/tts/_openai/_models/gpt-4o-mini-tts.yaml create mode 100644 src/agentscope/tts/_openai/_models/tts-1-hd.yaml create mode 100644 src/agentscope/tts/_openai/_models/tts-1.yaml create mode 100644 src/agentscope/tts/_tts_base.py create mode 100644 src/agentscope/tts/_tts_model_card.py create mode 100644 src/agentscope/tts/_tts_response.py create mode 100644 src/agentscope/types/__init__.py create mode 100644 src/agentscope/types/_hook.py create mode 100644 src/agentscope/types/_json.py create mode 100644 src/agentscope/types/_object.py create mode 100644 src/agentscope/workspace/__init__.py create mode 100644 src/agentscope/workspace/_base.py create mode 100644 src/agentscope/workspace/_docker/Dockerfile.node_copy.template create mode 100644 src/agentscope/workspace/_docker/Dockerfile.node_from.template create mode 100644 src/agentscope/workspace/_docker/Dockerfile.template create mode 100644 src/agentscope/workspace/_docker/__init__.py create mode 100644 src/agentscope/workspace/_docker/_docker_backend.py create mode 100644 src/agentscope/workspace/_docker/_docker_workspace.py create mode 100644 src/agentscope/workspace/_docker/_make_dockerfile.py create mode 100644 src/agentscope/workspace/_e2b/__init__.py create mode 100644 src/agentscope/workspace/_e2b/_constants.py create mode 100644 src/agentscope/workspace/_e2b/_e2b_backend.py create mode 100644 src/agentscope/workspace/_e2b/_e2b_workspace.py create mode 100644 src/agentscope/workspace/_gateway_client.py create mode 100644 src/agentscope/workspace/_gateway_shim.py create mode 100644 src/agentscope/workspace/_k8s/__init__.py create mode 100644 src/agentscope/workspace/_k8s/_constants.py create mode 100644 src/agentscope/workspace/_k8s/_k8s_backend.py create mode 100644 src/agentscope/workspace/_k8s/_k8s_workspace.py create mode 100644 src/agentscope/workspace/_local_workspace.py create mode 100644 src/agentscope/workspace/_mcp_gateway/__init__.py create mode 100644 src/agentscope/workspace/_mcp_gateway/__main__.py create mode 100644 src/agentscope/workspace/_mcp_gateway/_mcp_gateway_app.py create mode 100644 src/agentscope/workspace/_offload_protocol.py create mode 100644 src/agentscope/workspace/_opensandbox/__init__.py create mode 100644 src/agentscope/workspace/_opensandbox/_constants.py create mode 100644 src/agentscope/workspace/_opensandbox/_opensandbox_backend.py create mode 100644 src/agentscope/workspace/_opensandbox/_opensandbox_workspace.py create mode 100644 src/agentscope/workspace/_sandboxed_base.py create mode 100644 src/agentscope/workspace/_utils.py create mode 100644 tests/agent_basic_test.py create mode 100644 tests/agent_interrupt_test.py create mode 100644 tests/agui_protocol_test.py create mode 100644 tests/app_lifespan_dedicated_test.py create mode 100644 tests/backend_docker_test.py create mode 100644 tests/backend_e2b_test.py create mode 100644 tests/backend_local_test.py create mode 100644 tests/backend_opensandbox_test.py create mode 100644 tests/blob_store_s3_test.py create mode 100644 tests/builtin_bash_test.py create mode 100644 tests/builtin_edit_test.py create mode 100644 tests/builtin_file_cache_test.py create mode 100644 tests/builtin_glob_test.py create mode 100644 tests/builtin_grep_test.py create mode 100644 tests/builtin_read_test.py create mode 100644 tests/builtin_write_test.py create mode 100644 tests/compress_context_test.py create mode 100644 tests/compress_tool_result_test.py create mode 100644 tests/docker/k8s_workspace_test.Dockerfile create mode 100644 tests/embedding_dashscope_test.py create mode 100644 tests/embedding_gemini_test.py create mode 100644 tests/embedding_ollama_test.py create mode 100644 tests/embedding_openai_test.py create mode 100644 tests/event_test.py create mode 100644 tests/event_to_message_test.py create mode 100644 tests/formatter_anthropic_test.py create mode 100644 tests/formatter_dashscope_test.py create mode 100644 tests/formatter_deepseek_test.py create mode 100644 tests/formatter_gemini_test.py create mode 100644 tests/formatter_moonshot_test.py create mode 100644 tests/formatter_ollama_test.py create mode 100644 tests/formatter_openai_chat_test.py create mode 100644 tests/formatter_openai_response_test.py create mode 100644 tests/formatter_xai_test.py create mode 100644 tests/hitl_external_execution_test.py create mode 100644 tests/hitl_mixed_interrupt.py create mode 100644 tests/hitl_user_confirmation_test.py create mode 100644 tests/id_factory_test.py create mode 100644 tests/in_memory_message_bus_test.py create mode 100644 tests/index_worker_lease_test.py create mode 100644 tests/mcp_sse_client_test.py create mode 100644 tests/mcp_streamable_http_client_test.py create mode 100644 tests/mem0_agentscope_adapter_test.py create mode 100644 tests/mem0_middleware_test.py create mode 100644 tests/message_test.py create mode 100644 tests/middleware_background_acting_test.py create mode 100644 tests/middleware_budget_test.py create mode 100644 tests/middleware_filesystem_memory_test.py create mode 100644 tests/middleware_rag_test.py create mode 100644 tests/middleware_test.py create mode 100644 tests/model_anthropic_test.py create mode 100644 tests/model_base_test.py create mode 100644 tests/model_count_tokens_test.py create mode 100644 tests/model_dashscope_test.py create mode 100644 tests/model_deepseek_test.py create mode 100644 tests/model_gemini_test.py create mode 100644 tests/model_moonshot_test.py create mode 100644 tests/model_ollama_test.py create mode 100644 tests/model_openai_chat_test.py create mode 100644 tests/model_openai_response_test.py create mode 100644 tests/model_response_test.py create mode 100644 tests/model_xai_test.py create mode 100644 tests/permission_bash_parser_test.py create mode 100644 tests/permission_engine_test.py create mode 100644 tests/permission_mode_test.py create mode 100644 tests/rag_chunker_approx_token_test.py create mode 100644 tests/rag_parser_test.py create mode 100644 tests/rag_vdb_milvus_lite_test.py create mode 100644 tests/rag_vdb_mongodb_test.py create mode 100644 tests/rag_vdb_qdrant_test.py create mode 100644 tests/reme_middleware_test.py create mode 100644 tests/resource_access_policy_test.py create mode 100644 tests/service_agent_interrupt_test.py create mode 100644 tests/service_cancel_dispatcher_test.py create mode 100644 tests/service_enqueue_index_task_test.py create mode 100644 tests/service_inbox_middleware_test.py create mode 100644 tests/service_index_task_consumer_test.py create mode 100644 tests/service_knowledge_base_upload_test.py create mode 100644 tests/service_message_bus_test.py create mode 100644 tests/service_scheduler_test.py create mode 100644 tests/service_subagent_hitl_projector_test.py create mode 100644 tests/service_team_tools_test.py create mode 100644 tests/service_toolkit_test.py create mode 100644 tests/service_wakeup_dispatcher_test.py create mode 100644 tests/skill_loader_test.py create mode 100644 tests/storage_redis_knowledge_base_test.py create mode 100644 tests/storage_redis_test.py create mode 100644 tests/task_tool_test.py create mode 100644 tests/test_template.py create mode 100644 tests/tool_middleware_test.py create mode 100644 tests/tool_offload_middleware_test.py create mode 100644 tests/toolkit_skill_test.py create mode 100644 tests/toolkit_task_test.py create mode 100644 tests/toolkit_test.py create mode 100644 tests/tracing_test.py create mode 100644 tests/tts_dashscope_test.py create mode 100644 tests/tts_middleware_test.py create mode 100644 tests/tts_openai_test.py create mode 100644 tests/utils.py create mode 100644 tests/workspace_docker_test.py create mode 100644 tests/workspace_e2b_test.py create mode 100644 tests/workspace_k8s_test.py create mode 100644 tests/workspace_local_test.py create mode 100644 tests/workspace_opensandbox_test.py diff --git a/.gemini/styleguide.md b/.gemini/styleguide.md new file mode 100644 index 0000000..5462e0f --- /dev/null +++ b/.gemini/styleguide.md @@ -0,0 +1,96 @@ +# AgentScope Code Review Guide + +You should conduct a strict code review. Each requirement is labeled with priority: +- **[MUST]** must be satisfied or PR will be rejected +- **[SHOULD]** strongly recommended +- **[MAY]** optional suggestion + +## 1. Code Quality + +### [MUST] Lazy Loading +- Third-party library dependencies should be imported at the point of use, avoid centralized imports at file top + - The `Third-party library` refers to libraries not included in the `dependencies` variable in `pyproject.toml`. +- For base class imports, use factory pattern: +```python +def get_xxx_cls() -> "MyClass": + from xxx import BaseClass + class MyClass(BaseClass): ... + return MyClass +``` + +### [SHOULD] Code Conciseness +After understanding the code intent, check if it can be optimized: +- Avoid unnecessary temporary variables +- Merge duplicate code blocks +- Prioritize reusing existing utility functions + +### [MUST] Encapsulation Standards +- All Python files under `src/agentscope` should be named with `_` prefix, and exposure controlled through `__init__.py` +- Classes and functions used internally by the framework that don't need to be exposed to users must be named with `_` prefix + +## 2. [MUST] Code Security +- Prohibit hardcoding API keys/tokens/passwords +- Use environment variables or configuration files for management +- Check for debug information and temporary credentials +- Check for injection attack risks (SQL/command/code injection, etc.) + +## 3. [MUST] Testing & Dependencies +- New features must include unit tests +- New dependencies need to be added to the corresponding section in `pyproject.toml` +- Dependencies for non-core scenarios should not be added to the minimal dependency list + +## 4. Code Standards + +### [MUST] Comment Standards +- **Use English** +- All classes/methods must have complete docstrings, strictly following the template: +```python +def func(a: str, b: int | None = None) -> str: + """{description} + + Args: + a (`str`): + The argument a + b (`int | None`, optional): + The argument b + + Returns: + `str`: + The return str + """ +``` +- Use reStructuredText syntax for special content: +```python +class MyClass: + """xxx + + `Example link `_ + + .. note:: Example note + + .. tip:: Example tip + + .. important:: Example important info + + .. code-block:: python + + def hello_world(): + print("Hello world!") + + """ +``` + +### [MUST] Pre-commit Checks +- **Strict review**: In most cases, code should be modified rather than skipping checks +- **File-level check skipping is prohibited** +- Only allowed skip: agent class system prompt parameters (to avoid `\n` formatting issues) + +--- + +## 5. Git Standards + +### [MUST] PR Title +- Follow Conventional Commits +- Must use prefixes: `feat/fix/docs/ci/refactor/test`, etc. +- Format: `feat(scope): description` +- Example: `feat(memory): add redis cache support` \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..bd69010 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,67 @@ +name: Bug Report +description: Report a bug to help us improve AgentScope. +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + **AgentScope is an open-source project. To involve a broader community, we recommend asking your questions in English.** + + - type: checkboxes + id: prerequisites + attributes: + label: Prerequisites + description: Please confirm the following before submitting. + options: + - label: I have searched the existing [issues](https://github.com/agentscope-ai/agentscope/issues) and [discussions](https://github.com/agentscope-ai/agentscope/discussions), and this is not a duplicate. + required: true + - label: This is a bug, not a usage question. (For questions, please use [Discussions](https://github.com/agentscope-ai/agentscope/discussions/new?category=general) instead.) + required: true + + - type: textarea + id: description + attributes: + label: Background / Description + description: A clear and concise description of the bug, including what you were trying to do and the expected behavior. + placeholder: What were you trying to do, what did you expect, and what actually happened? + validations: + required: true + + - type: textarea + id: error + attributes: + label: Error Messages + description: Detailed error messages, stack traces, or logs. + render: shell + validations: + required: true + + - type: textarea + id: reproduce + attributes: + label: Steps to Reproduce + description: Provide a minimal reproducible example (code + commands) so we can reproduce the bug. + placeholder: | + 1. Code: + + ```python + ... + ``` + + 2. Run: `python ...` + 3. See error + validations: + required: true + + - type: textarea + id: environment + attributes: + label: Environment + description: Run `import agentscope; print(agentscope.__version__)` to get the AgentScope version. + value: | + - AgentScope Version: + - Python Version: + - OS: + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..0ec688e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Feature Request / Idea + url: https://github.com/agentscope-ai/agentscope/discussions/new?category=ideas + about: Suggest a new feature or share an idea. Maintainers will turn accepted proposals into roadmap issues. + - name: Question / Usage Help + url: https://github.com/agentscope-ai/agentscope/discussions/new?category=general + about: Ask a usage question or start a general discussion. Please do NOT open an issue for questions. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..b74281d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,25 @@ +## PR Title Format + +Please ensure your PR title follows the Conventional Commits format: +- Format: `(): ` +- Example: `feat(memory): add redis cache support` +- Allowed types: `feat`, `fix`, `docs`, `ci`, `refactor`, `test`, `chore`, `perf`, `style`, `build`, `revert` +- Description should start with a lowercase letter + +## AgentScope Version + +[The version of AgentScope you are working on, e.g. `import agentscope; print(agentscope.__version__)`] + +## Description + +[Please describe the background, purpose, changes made, and how to test this PR] + +## Checklist + +Please check the following items before code is ready to be reviewed. + +- [ ] An issue has been created for this PR +- [ ] I have read the [CONTRIBUTING.md](https://github.com/agentscope-ai/agentscope/blob/main/CONTRIBUTING.md) +- [ ] Docstrings are in Google style +- [ ] Related documentation has been updated (e.g. links, examples, etc.) in [documentation repository](https://github.com/agentscope-ai/docs) +- [ ] Code is ready for review \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..5a8062e --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,97 @@ +# AgentScope Code Review Guide + +You should conduct a strict code review. Each requirement is labeled with priority: +- **[MUST]** must be satisfied or PR will be rejected +- **[SHOULD]** strongly recommended +- **[MAY]** optional suggestion + +## 1. Code Quality + +### [MUST] Lazy Loading +- **Third-party library** dependencies should be imported at the point of use, avoid centralized imports at file top + - The `Third-party library` refers to libraries not included in the `dependencies` variable in `pyproject.toml`. +- For base class imports, use factory pattern: +```python +def get_xxx_cls() -> "MyClass": + from xxx import BaseClass + class MyClass(BaseClass): ... + return MyClass +``` + +### [SHOULD] Code Conciseness +After understanding the code intent, check if it can be optimized: +- Avoid unnecessary temporary variables +- Merge duplicate code blocks +- Prioritize reusing existing utility functions + +### [MUST] Encapsulation Standards +- All Python files under `src/agentscope` should be named with `_` prefix, and exposure controlled through `__init__.py` +- Classes and functions used internally by the framework that don't need to be exposed to users must be named with `_` prefix + +## 2. [MUST] Code Security +- Prohibit hardcoding API keys/tokens/passwords +- Use environment variables or configuration files for management +- Check for debug information and temporary credentials +- Check for injection attack risks (SQL/command/code injection, etc.) + +## 3. [MUST] Testing & Dependencies +- New features must include unit tests +- New dependencies need to be added to the corresponding section in `pyproject.toml` +- Dependencies for non-core scenarios should not be added to the minimal dependency list +- Test assertions MUST compare the entire data structure rather than asserting individual fields, so reviewers can quickly understand the test intent. For random or non-deterministic fields, use `AnyString` and `AnyValue` from `tests/utils.py` for matching + +## 4. Code Standards + +### [MUST] Comment Standards +- **Use English** +- All classes/methods must have complete docstrings, strictly following the template: +```python +def func(a: str, b: int | None = None) -> str: + """{description} + + Args: + a (`str`): + The argument a + b (`int | None`, optional): + The argument b + + Returns: + `str`: + The return str + """ +``` +- Use reStructuredText syntax for special content: +```python +class MyClass: + """xxx + + `Example link `_ + + .. note:: Example note + + .. tip:: Example tip + + .. important:: Example important info + + .. code-block:: python + + def hello_world(): + print("Hello world!") + + """ +``` + +### [MUST] Pre-commit Checks +- **Strict review**: In most cases, code should be modified rather than skipping checks +- **File-level check skipping is prohibited** +- Only allowed skip: agent class system prompt parameters (to avoid `\n` formatting issues) + +--- + +## 5. Git Standards + +### [MUST] PR Title +- Follow Conventional Commits +- Must use prefixes: `feat/fix/docs/ci/refactor/test`, etc. +- Format: `feat(scope): description` +- Example: `feat(memory): add redis cache support` \ No newline at end of file diff --git a/.github/scripts/update_news.py b/.github/scripts/update_news.py new file mode 100755 index 0000000..9dccc0e --- /dev/null +++ b/.github/scripts/update_news.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Script to automatically update NEWS section in README files. +Reads the first 10 news items from docs/NEWS.md and updates README.md and +README_zh.md. +""" + +from pathlib import Path + + +def read_news_items(news_file: Path, max_items: int = 10) -> list[str]: + """ + Read news items from NEWS.md file. + + Args: + news_file (`Path`): + Path to the NEWS.md file + max_items (`int`, optional): + Maximum number of items to read + + Returns: + `list[str]`: + List of news items + """ + with open(news_file, "r", encoding="utf-8") as f: + content = f.read() + + # Split by lines that start with "- **[" + lines = content.strip().split("\n") + news_items = [] + + for line in lines: + if line.strip().startswith("- **["): + news_items.append(line) + if len(news_items) >= max_items: + break + + return news_items + + +def update_readme( + readme_file: Path, + news_items: list[str], +) -> None: + """ + Update the NEWS section in README file using HTML comment markers. + + Args: + readme_file (`Path`): + Path to the README file + news_items (`list[str]`): + List of news items to insert + """ + with open(readme_file, "r", encoding="utf-8") as f: + content = f.read() + + # Use HTML comment markers to identify the NEWS section + begin_marker = "" + end_marker = "" + + if begin_marker not in content or end_marker not in content: + print(f"⚠️ NEWS markers not found in {readme_file.name}") + print( + f" Please add '{begin_marker}' and '{end_marker}' to mark the " + f"NEWS section", + ) + return + + # Find positions of markers + begin_pos = content.find(begin_marker) + end_pos = content.find(end_marker) + + if begin_pos == -1 or end_pos == -1 or begin_pos >= end_pos: + print(f"❌ Invalid NEWS markers in {readme_file.name}") + return + + # Create new NEWS content + news_content = "\n".join(news_items) + + # Replace content between markers + new_content = ( + content[: begin_pos + len(begin_marker)] + + "\n" + + news_content + + "\n" + + content[end_pos:] + ) + + with open(readme_file, "w", encoding="utf-8") as f: + f.write(new_content) + + print(f"✅ Updated {readme_file.name}") + + +def main() -> None: + """Main function to update NEWS in README files.""" + # Define paths + repo_root = Path(__file__).parent.parent.parent + news_file_en = repo_root / "docs" / "NEWS.md" + news_file_zh = repo_root / "docs" / "NEWS_zh.md" + readme_en = repo_root / "README.md" + readme_zh = repo_root / "README_zh.md" + + # Update English README from NEWS.md + if news_file_en.exists(): + print(f"📖 Reading news items from {news_file_en}") + news_items_en = read_news_items(news_file_en, max_items=10) + print(f"📰 Found {len(news_items_en)} English news items") + + if news_items_en and readme_en.exists(): + print(f"📝 Updating {readme_en.name}...") + update_readme(readme_en, news_items_en) + elif not news_items_en: + print("⚠️ No English news items found") + else: + print(f"⚠️ {readme_en} not found") + else: + print(f"❌ NEWS.md not found at {news_file_en}") + + # Update Chinese README from NEWS_zh.md + if news_file_zh.exists() and news_file_zh.stat().st_size > 0: + print(f"📖 Reading news items from {news_file_zh}") + news_items_zh = read_news_items(news_file_zh, max_items=10) + print(f"📰 Found {len(news_items_zh)} Chinese news items") + + if news_items_zh and readme_zh.exists(): + print(f"📝 Updating {readme_zh.name}...") + update_readme(readme_zh, news_items_zh) + elif not news_items_zh: + print("⚠️ No Chinese news items found") + else: + print(f"⚠️ {readme_zh} not found") + else: + print( + f"⚠️ NEWS_zh.md not found or empty at {news_file_zh}, " + f"using English news for Chinese README", + ) + # Fallback: use English news for Chinese README if NEWS_zh.md + # doesn't exist + if news_file_en.exists() and readme_zh.exists(): + print(f"📖 Reading news items from {news_file_en} (fallback)") + news_items = read_news_items(news_file_en, max_items=10) + if news_items: + print(f"📝 Updating {readme_zh.name} with English news...") + update_readme(readme_zh, news_items) + + print("✨ All done!") + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/github-monitor.yml b/.github/workflows/github-monitor.yml new file mode 100644 index 0000000..777c721 --- /dev/null +++ b/.github/workflows/github-monitor.yml @@ -0,0 +1,63 @@ +# ───────────────────────────────────────────────────────────────────────────── +# Template: copy this file to the MONITORED repo at +# .github/workflows/github-monitor.yml +# +# Required secrets in the monitored repo: +# - STORAGE_REPO_TOKEN Fine-grained PAT with Contents: Read & Write on +# the storage repo (e.g. DavdGao/agentscope-monitor). +# - DINGTALK_WEBHOOK Dingtalk bot webhook URL. +# - DINGTALK_SECRET (optional) Dingtalk bot signing secret. +# ───────────────────────────────────────────────────────────────────────────── +name: GitHub Monitor + +on: + issues: + types: [opened, closed, reopened] + issue_comment: + types: [created] + pull_request_target: + # synchronize is silent (stored only) — see SILENT_REALTIME_EVENTS in scripts/config.py. + # It's needed for the daily "PRs with new commits to re-review" section. + types: [opened, closed, reopened, synchronize, review_requested] + pull_request_review: + types: [submitted] + pull_request_review_comment: + types: [created] + discussion: + types: [created, answered] + discussion_comment: + types: [created] + +permissions: + contents: read + +jobs: + notify: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout storage repo (notification scripts) + uses: actions/checkout@v4 + with: + repository: DavdGao/agentscope-monitor + token: ${{ secrets.STORAGE_REPO_TOKEN }} + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: pip + + - run: pip install -r requirements.txt + + - name: Send notification + env: + EVENT_NAME: ${{ github.event_name }} + EVENT_PAYLOAD: ${{ toJson(github.event) }} + REPO_NAME: ${{ github.repository }} + RUN_ID: ${{ github.run_id }} + RUN_ATTEMPT: ${{ github.run_attempt }} + STORAGE_REPO: DavdGao/agentscope-monitor + STORAGE_REPO_TOKEN: ${{ secrets.STORAGE_REPO_TOKEN }} + DINGTALK_WEBHOOK: ${{ secrets.DINGTALK_WEBHOOK }} + DINGTALK_SECRET: ${{ secrets.DINGTALK_SECRET }} + run: python -m scripts.notify diff --git a/.github/workflows/k8s-workspace.yml b/.github/workflows/k8s-workspace.yml new file mode 100644 index 0000000..ec091a5 --- /dev/null +++ b/.github/workflows/k8s-workspace.yml @@ -0,0 +1,81 @@ +name: K8s Workspace Integration Tests + +# Runs the K8sWorkspace integration suite against a kind cluster. +# The regular `unittest.yml` job also imports this test file, but every +# test inside is guarded by `skipUnless(_KIND_AVAILABLE, ...)`, so it is +# a no-op there. This workflow is where the tests actually execute. + +on: + push: + paths: + - "src/agentscope/workspace/**" + - "tests/workspace_k8s_test.py" + - "tests/docker/k8s_workspace_test.Dockerfile" + - ".github/workflows/k8s-workspace.yml" + pull_request: + paths: + - "src/agentscope/workspace/**" + - "tests/workspace_k8s_test.py" + - "tests/docker/k8s_workspace_test.Dockerfile" + - ".github/workflows/k8s-workspace.yml" + +jobs: + k8s-workspace-tests: + if: false == contains(github.event.pull_request.title, 'WIP') + # Ubuntu only: kind requires linux + docker; the test module's + # skipUnless would skip everything on Windows/macOS anyway. + runs-on: ubuntu-latest + timeout-minutes: 25 + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + python-version: '3.11' + + - name: Create kind cluster + uses: helm/kind-action@v1 + with: + cluster_name: agentscope-test + wait: 120s + + - name: Build K8s workspace test image + run: | + docker build \ + -f tests/docker/k8s_workspace_test.Dockerfile \ + -t agentscope-k8s-test:ci . + + - name: Load image into kind + run: kind load docker-image agentscope-k8s-test:ci --name agentscope-test + + - name: Create test namespace + run: kubectl create namespace agentscope + + - name: Install project + run: | + uv pip install -q -e .[dev] + uv pip install pytest + + - name: Run K8s workspace tests + env: + K8S_TEST_IMAGE: agentscope-k8s-test:ci + K8S_TEST_NAMESPACE: agentscope + run: pytest tests/workspace_k8s_test.py -v + + - name: Dump cluster diagnostics on failure + if: failure() + run: | + echo "── pods ──" + kubectl get pods -A -o wide || true + echo "── pvcs ──" + kubectl get pvc -A || true + echo "── events ──" + kubectl get events -A --sort-by=.lastTimestamp | tail -n 50 || true + echo "── describe agentscope pods ──" + for p in $(kubectl -n agentscope get pods -o name 2>/dev/null); do + echo "── $p ──" + kubectl -n agentscope describe "$p" || true + kubectl -n agentscope logs "$p" --all-containers=true --tail=200 || true + done + diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml new file mode 100644 index 0000000..e0f7f40 --- /dev/null +++ b/.github/workflows/pr-title-check.yml @@ -0,0 +1,49 @@ +name: PR Title Check + +on: + pull_request: + branches: + - main + types: [opened, edited, synchronize, reopened] + +jobs: + check-pr-title: + runs-on: ubuntu-latest + steps: + - name: Check PR Title Format + uses: amannn/action-semantic-pull-request@v6.1.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + # Configure allowed types based on your requirements + types: | + feat + fix + docs + ci + refactor + test + chore + perf + style + build + revert + # Require a scope (the part in parentheses) + requireScope: false + # Scope pattern: only lowercase letters, numbers, hyphens, and underscores allowed + scopePattern: ^[a-z0-9_-]+$ + scopePatternError: | + The scope (text in parentheses) must contain only lowercase letters, numbers, hyphens, and underscores. + Example: "feat(memory): add redis cache support" + Invalid: "feat(Memory): ..." or "feat(MEMORY): ..." + # Subject (description) must not be empty and must be lowercase + subjectPattern: ^(?![A-Z]).+$ + subjectPatternError: | + The subject (description after colon) must start with a lowercase letter. + Example: "feat(memory): add redis cache support" + # Validate the entire PR title against the Conventional Commits spec + validateSingleCommit: false + # Ignore merge commits + ignoreLabels: | + ignore-semantic-pull-request + diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..475e7f3 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,35 @@ +name: Pre-commit + +on: [push, pull_request] + +jobs: + run: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: True + matrix: + os: [ubuntu-latest] + env: + OS: ${{ matrix.os }} + PYTHON: '3.11' + steps: + - uses: actions/checkout@master + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + python-version: '3.11' + - name: Install AgentScope + run: | + uv pip install -q -e .[dev] + - name: Install pre-commit + run: | + pre-commit install + - name: Pre-commit starts + run: | + pre-commit run --all-files > pre-commit.log 2>&1 || true + cat pre-commit.log + if grep -q Failed pre-commit.log; then + echo -e "\e[41m [**FAIL**] Please install pre-commit and format your code first. \e[0m" + exit 1 + fi + echo -e "\e[46m ********************************Passed******************************** \e[0m" diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml new file mode 100644 index 0000000..58f4f08 --- /dev/null +++ b/.github/workflows/publish-pypi.yml @@ -0,0 +1,43 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Publish PyPi Package + +on: + workflow_dispatch: + release: + types: [published] + +permissions: + contents: read + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + python-version: '3.11' + - name: Install dependencies + run: | + uv pip install build + - name: Build package + run: python -m build + - name: Test installation + run: | + uv pip install dist/*.whl + python -c "import agentscope; print(agentscope.__version__)" + - name: Publish package + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..f9fe159 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,35 @@ +# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. +# +# You can adjust the behavior by modifying this file. +# For more information, see: +# https://github.com/actions/stale +name: Mark stale issues and pull requests + +on: + schedule: + - cron: '30 9 * * *' + +jobs: + stale: + + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - uses: actions/stale@v10 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue is marked as stale because there has been no activity for 60 days. Remove stale label or add new comments or this issue will be closed in 90 day.' + close-issue-message: 'Close this stale issue.' + stale-issue-label: 'stale-issue' + exempt-issue-labels: 'RoadMap,Roadmap' + days-before-stale: 60 + days-before-close: 30 + + stale-pr-message: 'This PR is marked as stale because there has been no activity for 60 days. Remove stale label or add new comments or this PR will be closed in 30 days.' + close-pr-message: 'Close this stale PR.' + stale-pr-label: 'stale-pr' + days-before-pr-stale: 60 + days-before-pr-close: 30 diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml new file mode 100644 index 0000000..3b9fd77 --- /dev/null +++ b/.github/workflows/unittest.yml @@ -0,0 +1,32 @@ +name: Python Unittest Coverage + +on: [push, pull_request] + +jobs: + test: + if: false == contains(github.event.pull_request.title, 'WIP') + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-15] + python-version: ['3.11'] + env: + OS: ${{ matrix.os }} + steps: + - uses: actions/checkout@master + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install Dev Dependencies + run: | + uv pip install -q -e .[dev] + uv pip install coverage pytest + - name: Run tests with coverage + env: + GRPC_VERBOSITY: ERROR + run: | + coverage run -m pytest tests + - name: Generate coverage report + run: | + coverage report -m \ No newline at end of file diff --git a/.github/workflows/update_news.yml b/.github/workflows/update_news.yml new file mode 100644 index 0000000..4f609b4 --- /dev/null +++ b/.github/workflows/update_news.yml @@ -0,0 +1,39 @@ +name: Update NEWS in README +on: + push: + paths: + - 'docs/NEWS.md' + - 'docs/NEWS_zh.md' + branches-ignore: + - 'main' + +# Prevent concurrent runs that modify README files +concurrency: + group: readme-updates-${{ github.ref }} + cancel-in-progress: false + +jobs: + updateNews: + name: NEWS Updater + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.10' + + - name: Update NEWS in README files + run: python .github/scripts/update_news.py + + - name: Commit changes + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git add README.md README_zh.md + git diff --staged --quiet || git commit -m "docs: auto-sync NEWS section to README files" + git push + diff --git a/.github/workflows/web-ui.yml b/.github/workflows/web-ui.yml new file mode 100644 index 0000000..7572e80 --- /dev/null +++ b/.github/workflows/web-ui.yml @@ -0,0 +1,46 @@ +name: Web UI + +on: + push: + branches: + - main + paths: + - ".github/workflows/web-ui.yml" + - "examples/web_ui/**" + pull_request: + branches: + - main + paths: + - ".github/workflows/web-ui.yml" + - "examples/web_ui/**" + +jobs: + check: + if: github.event_name != 'pull_request' || false == contains(github.event.pull_request.title, 'WIP') + runs-on: ubuntu-latest + defaults: + run: + working-directory: examples/web_ui + steps: + - uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: examples/web_ui/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --no-frozen-lockfile + + - name: Check formatting + run: pnpm format:check + + - name: Build + run: pnpm build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..730e6c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,166 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +# JS/Node frontend uses lib/ as a source directory (shadcn convention). +# Re-include it so newly added files under examples/web_ui/frontend/src/lib/ +# aren't silently swallowed by the Python-oriented lib/ rule above. +!examples/web_ui/frontend/src/lib/ +!examples/web_ui/frontend/src/lib/** +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +.idea/ + +# macOS +.DS_Store + +# docs +docs/tutorial/en/build/ +docs/tutorial/zh_CN/build/ + +# Sphinx build artifacts +docs/tutorial/**/doctrees/ +docs/tutorial/**/.doctrees/ +*.buildinfo +*.pickle + +node_modules/ +package-lock.json +*.tsbuildinfo +.wireit/ +.angular/ +uv.lock + +# JS/Node frontend (examples/web_ui) +*.local +.eslintcache +.vite/ +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..fa20411 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,116 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-ast + - id: sort-simple-yaml + - id: check-yaml + exclude: | + (?x)^( + meta.yaml + )$ + - id: check-xml + - id: check-toml + - id: check-docstring-first + - id: check-json + exclude: | + (?x)^( + .*tsconfig(\.[^.]+)?\.json + )$ + - id: fix-encoding-pragma + - id: detect-private-key + - id: trailing-whitespace + - repo: https://github.com/asottile/add-trailing-comma + rev: v3.1.0 + hooks: + - id: add-trailing-comma + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.7.0 + hooks: + - id: mypy + exclude: + (?x)( + pb2\.py$ + | grpc\.py$ + | ^docs + | \.html$ + ) + args: [ --disallow-untyped-defs, + --disallow-incomplete-defs, + --ignore-missing-imports, + --disable-error-code=var-annotated, + --disable-error-code=union-attr, + --disable-error-code=assignment, + --disable-error-code=attr-defined, + --disable-error-code=import-untyped, + --disable-error-code=truthy-function, + --disable-error-code=typeddict-item, + --follow-imports=skip, + --explicit-package-bases, + ] + # - repo: https://github.com/numpy/numpydoc + # rev: v1.6.0 + # hooks: + # - id: numpydoc-validation + - repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black + args: [--line-length=79] + - repo: https://github.com/PyCQA/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + args: ["--extend-ignore=E203"] + exclude: ^docs + - repo: https://github.com/pylint-dev/pylint + rev: v3.0.2 + hooks: + - id: pylint + exclude: + (?x)( + ^docs + | pb2\.py$ + | grpc\.py$ + | \.demo$ + | \.md$ + | \.html$ + | ^examples/paper_llm_based_algorithm/ + ) + args: [ + --disable=W0511, + --disable=W0718, + --disable=W0122, + --disable=C0103, + --disable=R0913, + --disable=E0401, + --disable=E1101, + --disable=C0415, + --disable=W0603, + --disable=R1705, + --disable=R0914, + --disable=E0601, + --disable=W0602, + --disable=W0604, + --disable=R0801, + --disable=R0902, + --disable=R0903, + --disable=C0123, + --disable=W0231, + --disable=W1113, + --disable=W0221, + --disable=R0401, + --disable=W0632, + --disable=W0123, + --disable=C3001, + --max-branches=30, + --max-nested-blocks=7, + --max-statements=100, + --max-returns=10, + --max-module-lines=3000, + ] + - repo: https://github.com/regebro/pyroma + rev: "5.0" + hooks: + - id: pyroma + args: [--min=10, .] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f03ed62 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,420 @@ +# Contributing to AgentScope + + +Thank you for your interest in contributing to AgentScope! + +As an open-source project, we warmly welcome and encourage +contributions from the community. Whether you're fixing bugs, adding new features, improving documentation, or sharing +ideas, your contributions help make AgentScope better for everyone. + +## 1. Development Roadmap and How to Get Involved + +To support the long-term, healthy growth of AgentScope and its open-source +community, we keep our development plan transparent and openly tracked. + +**Our roadmap is public.** The AgentScope development plan is published and +continuously updated on our [GitHub Projects page](https://github.com/orgs/agentscope-ai/projects/2). +The roadmap reflects the technical direction set by the core team, who are +responsible for AgentScope's overall design and quality. + +**Tasks open to the community.** Items labeled `help wanted` on the Projects +page/issues are contribution opportunities open to everyone. If one of these +interests you: + +- Comment on the related issue to let us know you'd like to take it on +- This helps us avoid duplicate efforts and coordinate with you early + +**If you'd like to join the core development.** We warmly welcome contributors +who want to go deeper and help shape AgentScope itself. Over time, we plan to +gradually invite committed contributors into the core development circle. +Before reaching out, we'd like to share a few honest expectations so you can +decide whether it's a good fit right now: + +- Core development involves frequent design discussions, code reviews, and + iterative revisions — it asks for a sustained investment of time and energy +- To keep AgentScope cohesive and reliable, the core team retains + responsibility for the project's technical direction and quality bar; core + contributors work within this collaborative process + +If this fits your situation, please reach out to the core developers — we'd +love to talk. + +**Proposing something new.** If you have an idea that isn't on the roadmap +yet, please open a new issue describing your proposal. The core team will +respond and discuss it with you so we can find the best path forward together. + +## 2. Responsible Use of AI in Contributions + +AgentScope welcomes contributors who use AI coding assistants — Claude Code, +Cursor, Codex, Copilot, and others. We just ask that they be used +**responsibly**. AgentScope is sustained by reviewer time and community +trust, and AI-assisted contributions need to honor both. + +A few expectations when AI is involved in your work: + +- **You — not the AI — are the author.** Read the diff line by line, run it, + and make sure you understand *what* changed and *why* before you push. + "Claude Code / Cursor / Codex told me to do it" is not an acceptable + answer in code review, and is not the kind of behavior that builds a + healthy open-source community. PRs whose authors cannot explain their own + changes will be closed. + +- **Review your AI-generated code before opening a PR.** Reviewer time is + the most precious resource in this project. Don't outsource your own + review to the maintainers by dumping unreviewed AI output into a PR. + +- **Keep PRs atomic.** Do not submit a 10K+-line PR produced by an AI in a + single shot. Such PRs are unreviewable and will be rejected. Break the + work into focused, single-purpose PRs the same way a human contributor + would. + +- **AI-assisted code follows the same rules.** All of AgentScope's + development principles — modularity, lazy imports, conventional commits, + test coverage, no surprise API breaks — apply identically to code written + with AI assistance. AI is not an excuse for skipping conventions. + +The goal is simple: AI helps you move faster, but the responsibility for +what lands in AgentScope still rests with you as a human contributor. + +## 3. Contribution Workflow + +End-to-end, contributing a change to AgentScope looks like this. + +### Step 1. Claim or create an issue + +Before writing code, find or open the issue that frames your work. + +- **Working on an existing item?** Browse [Projects](https://github.com/orgs/agentscope-ai/projects/2) + and [Issues](https://github.com/agentscope-ai/agentscope/issues) for items + labeled `help wanted` (see [§1](#1-development-roadmap-and-how-to-get-involved)). + Comment on the issue to claim it before starting. +- **Proposing something new?** Open a new issue describing the problem, + your proposed solution, and any design alternatives. Wait for feedback + from the core team before starting a non-trivial implementation — this + avoids wasted rewrites. + +### Step 2. Fork the repo and create a development branch + +1. Fork [agentscope-ai/agentscope](https://github.com/agentscope-ai/agentscope) on GitHub. +2. Clone your fork and add the upstream remote: + ```bash + git clone https://github.com//agentscope.git + cd agentscope + git remote add upstream https://github.com/agentscope-ai/agentscope.git + ``` +3. Create a topic branch off the latest `main`: + ```bash + git checkout main + git pull upstream main + git checkout -b feat/ + ``` + Use a branch name aligned with the change type, e.g., `feat/redis-memory`, + `fix/react-agent-leak`, `docs/contributing-update`. + +### Step 3. Set up your local environment + +AgentScope requires **Python 3.11+** (see `pyproject.toml`). + +```bash +# Create an isolated environment (uv shown; virtualenv / conda also fine) +uv venv +source .venv/bin/activate + +# Install AgentScope in editable mode with the dev extras +pip install -e ".[dev]" +# or, equivalently, with uv: +uv pip install -e ".[dev]" + +# Enable the git pre-commit hooks +pre-commit install +``` + +The `dev` extra pulls in `pre-commit`, `pytest`, the documentation +toolchain, and the `full` extra (which itself includes `models`, `service`, +and `storage`). A single installation gives you everything needed to develop +and run the complete test suite. + +### Step 4. Develop + +A few conventions to follow while writing code: + +- **Lazy imports for optional dependencies.** Any dependency **not listed in + `[project.dependencies]` of `pyproject.toml`** — i.e., anything coming + from the optional groups (`gemini`, `ollama`, `xai`, `service`, `storage`, + etc.) — **must be lazy-imported** at point of use rather than at module + top level: + ```python + def some_function(): + import google.genai # from the `gemini` extra — lazy-imported + # ... use google.genai here + ``` + This keeps `import agentscope` lightweight, and `ImportError` surfaces + only when a feature actually relying on the extra is invoked. If your + change requires a brand-new dependency, decide first whether it belongs + in the base `[project.dependencies]` (always required, kept small) or in + one of the optional extras — and discuss it in the issue before merging. + +- **Follow the project's code style.** Pre-commit handles formatting and + most lint rules automatically. Don't fight the formatter. + +- **Write unit tests alongside features.** Tests live under `tests/` and + follow the existing structure. Tests that rely on an optional extra + (e.g., Redis, Ollama) should skip cleanly when that extra isn't + installed. + +### Step 5. Run pre-commit, tests, and update documentation + +Before opening the PR, run the same checks CI will run: + +```bash +# Auto-format and lint +pre-commit run --all-files + +# Run the unit tests +pytest tests +``` + +If a pre-commit hook fails, fix the issue (most fixes are applied +automatically) and re-stage the files. Don't bypass hooks with +`--no-verify`. + +**Update documentation alongside the code change.** + +- AgentScope's user-facing documentation lives in a separate repository: + **[agentscope-ai/docs](https://github.com/agentscope-ai/docs)**. If your + change affects user-facing behavior — new modules, new public APIs, + behavior changes, tutorials — please open a companion PR there. +- Update inline docstrings and example snippets for any new public APIs. +- Update `README.md` if your change affects how users get started or what + AgentScope advertises. + +### Step 6. Commit and open a pull request + +**Commit message format.** We follow the [Conventional Commits](https://www.conventionalcommits.org/) +specification. This keeps commit history readable and enables automatic +changelog generation. + +``` +(): +``` + +**Types:** +- `feat:` A new feature +- `fix:` A bug fix +- `docs:` Documentation only changes +- `style:` Changes that do not affect the meaning of the code (whitespace, formatting, etc.) +- `refactor:` A code change that neither fixes a bug nor adds a feature +- `perf:` A code change that improves performance +- `ci:` Adding missing tests or correcting existing tests +- `chore:` Changes to the build process or auxiliary tools and libraries + +**Examples:** +```bash +feat(models): add support for Claude-3 model +fix(agent): resolve memory leak in ReActAgent +docs(readme): update installation instructions +refactor(formatter): simplify message formatting logic +ci(models): add unit tests for OpenAI integration +``` + +**Pull request title format.** PR titles follow the same Conventional +Commits format and are validated automatically by GitHub Actions on PRs +against `main`. PRs with invalid titles will be blocked until corrected. + +``` +(): +``` + +**Requirements:** +- Title must start with one of: `feat`, `fix`, `docs`, `ci`, `refactor`, `test`, `chore`, `perf`, `style`, `build`, `revert` +- Scope is optional but recommended +- **Scope must be lowercase** — only lowercase letters, numbers, hyphens (`-`), and underscores (`_`) are allowed +- Description should start with a lowercase letter +- Keep the title concise and descriptive + +**Examples:** +``` +✅ Valid: +feat(memory): add redis cache support +fix(agent): resolve memory leak in ReActAgent +docs(tutorial): update installation guide +ci(workflow): add PR title validation +refactor(my-feature): simplify logic + +❌ Invalid: +feat(Memory): add cache # Scope must be lowercase +feat(MEMORY): add cache # Scope must be lowercase +feat(MyFeature): add feature # Scope must be lowercase +``` + +**Open the PR.** Push your branch to your fork and open a pull request +against `agentscope-ai/agentscope:main`. In the PR description: + +- Link the issue you claimed (`Fixes #123` or `Refs #123`) +- Summarize what changed and why +- Note any breaking changes, deprecations, or migration steps +- Link the companion docs PR in [agentscope-ai/docs](https://github.com/agentscope-ai/docs) + if you opened one + +## 4. Important Notices + +A few cross-cutting constraints worth knowing before you start a +contribution. Module-specific notices live in the corresponding module +guide below. + +- **Open an issue before non-trivial work.** Surprise PRs that touch many + files, change public APIs, or introduce a new module are difficult to + review and likely to be rejected. Discuss the design in an issue first. +- **Keep PRs focused and atomic.** One PR, one purpose. Don't bundle a + refactor with a feature, or a feature with an unrelated bug fix. +- **Don't break public APIs without notice.** Maintain backward + compatibility when you can. If a breaking change is unavoidable, call it + out clearly in the PR description and update the affected examples and + docs in the same PR. +- **Don't bypass the lazy import principle.** Optional dependencies must be + imported at point of use, not at module top level. +- **Don't add dependencies casually.** Every new dependency is a long-term + maintenance commitment. If a dependency is needed by only one module, + prefer a lazy import inside that module. +- **Don't ignore CI failures.** Pre-commit, type checks, and tests must + pass before a PR is ready for review. Don't push the burden of fixing + them onto the reviewer. +- **Be respectful.** Follow our Code of Conduct. AgentScope's review + culture is direct but kind, and we expect the same from contributors. + +## 5. Module-Specific Contribution Guides + +The notes below cover the modules most commonly extended by community +contributors. For other modules, please open an issue first so we can +coordinate. + +### Chat Model + +A chat model in AgentScope is more than a single class — to be usable +inside an `Agent`, it needs a small set of upstream/downstream pieces. +A complete chat-model contribution includes **all** of the following: + +1. **Credential class** — under `agentscope.credential`, subclassing + `CredentialBase`. Carries the API key, endpoint, and other auth fields + your SDK needs. + _Reference: `agentscope/credential/_anthropic.py`_ + +2. **Chat model class** — under `agentscope.model./`, subclassing + `ChatModelBase`. The implementation needs to cover: + - Both streaming and non-streaming modes + - Tools API integration (function/tool calling) + - The `tool_choice` argument + - Reasoning models, where applicable + + _Reference: `agentscope/model/_anthropic/`_ + +3. **Model card YAML(s)** — under + `agentscope.model.._models/`, one YAML per supported model. + Required fields: `name`, `label`, `status`, `input_types`, + `output_types`, `context_size`, `output_size`. Optional: + `parameter_overrides`, `deprecated_at`. + + Example (`claude-sonnet-4-6.yaml`): + ```yaml + name: claude-sonnet-4-6 + label: Claude Sonnet 4.6 + status: active + input_types: + - text/plain + - image/jpeg + output_types: + - text/plain + context_size: 1000000 + output_size: 65536 + parameter_overrides: + max_tokens: {"maximum": 65536} + ``` + +4. **Formatter classes** — under `agentscope.formatter`, both subclassing + `FormatterBase`. Two variants are required because some APIs treat + multi-agent conversations differently from single-user chat: + - `ChatFormatter` for single-user chat scenarios + - `MultiAgentFormatter` for multi-agent scenarios + + Each formatter converts `Msg` objects into the request format the + provider's API expects. + _Reference: `agentscope/formatter/_anthropic_formatter.py`_ + +> ⚠️ PRs that add only the model class without the matching credential, +> model card YAML, and both formatter variants will not be merged. + +### Agent + +AgentScope deliberately maintains a **single core agent class** — +`agentscope.agent.Agent` — that integrates all functionality of the +AgentScope library (memory, tools, MCP, formatters, models, etc.). + +For specialized or domain-specific agents, please contribute them as +[examples](#examples) rather than as new classes in `agentscope.agent`. + +If you believe a use case genuinely requires a new top-level agent class: + +1. **Open an issue first** describing the use case and explaining why + composing existing `Agent` capabilities is insufficient. +2. **Wait for design discussion** with the core team before starting any + implementation. +3. PRs that introduce a new agent class without prior discussion will be + rejected. + +### Workspace + +A Workspace provides the runtime context an agent operates in (skills, +scheduled tasks, etc.). Adding a new workspace backend requires two +classes plus documentation: + +1. **Workspace class** — under `agentscope.workspace`, subclassing + `WorkspaceBase`. Implements the storage and lifecycle semantics of + your backend. + _Reference: `agentscope/workspace/_local_workspace.py` (`LocalWorkspace`)_ + +2. **Workspace manager class** — alongside + `agentscope/app/_manager/_workspace_manager.py`, subclassing + `WorkspaceManagerBase`. Wires your workspace into the application + lifecycle. + _Reference: `LocalWorkspaceManager` in the same file._ + +3. **Documentation** — open a companion PR in + [agentscope-ai/docs](https://github.com/agentscope-ai/docs) describing + how to configure and use your workspace. + +### Examples + +We highly encourage contributions of new examples that showcase +AgentScope's capabilities. + +The `examples/` directory in the main repository focuses on +**demonstrating specific features and capabilities** — concise, +educational reference implementations. For more complete, production-style +applications, please contribute them to +**[agentscope-samples](https://github.com/agentscope-ai/agentscope-samples)** +instead. + +A new example should live in its own subdirectory: + +``` +examples/ +└── / + ├── main.py + ├── README.md # explain the example's purpose, how to run it, and expected output + └── ... +``` + +`examples/agent_service/` is a good starting reference. + +## Getting Help + +If you need assistance or have questions: + +- Open a [Discussion](https://github.com/agentscope-ai/agentscope/discussions) +- Report bugs via [Issues](https://github.com/agentscope-ai/agentscope/issues) +- Contact the maintainers at DingTalk or Discord (links in the README.md) + + +--- + +Thank you for contributing to AgentScope! Your efforts help build a better tool for the entire community. diff --git a/CONTRIBUTING_zh.md b/CONTRIBUTING_zh.md new file mode 100644 index 0000000..1244b60 --- /dev/null +++ b/CONTRIBUTING_zh.md @@ -0,0 +1,298 @@ +# 为 AgentScope 做贡献 + +感谢大家对 AgentScope 的关注! + +作为一个开源项目,我们欢迎并鼓励来自社区的贡献。无论是修复 bug、新增功能、完善文档,还是分享想法,每一份贡献都让 AgentScope 变得更好。 + +## 1. 开发路线图与参与方式 + +为了支持 AgentScope 开源社区的长期健康发展,我们将公开、透明地维护 AgentScope 的开发计划。 + +**路线图公开**。AgentScope 的开发计划会发布在 [GitHub Projects 页面](https://github.com/orgs/agentscope-ai/projects/2),并持续更新。路线图会反映 AgentScope 的技术发展方向,由核心开发团队对 AgentScope 的整体设计与质量负责。 + +**社区可认领的任务**。Projects 页面 / Issues 中标有 `help wanted` 的条目对所有人开放。如果你有兴趣参与某一项: + +- 请在对应 issue 下评论,告知准备认领 +- 这样可以避免重复劳动,也方便我们尽早协作 + +**成为核心开发者**。我们欢迎想要更深入参与、共同塑造 AgentScope 的开发者。我们会在合适的时机邀请投入度高的贡献者成为核心开发者。 +成为核心开发者也意味着更频繁的参与到 AgentScope 的开发工作中,包括: + +- 更加频繁的设计讨论、代码评审与多轮迭代,需要持续的时间和精力投入 +- 为保证 AgentScope 的整体一致性与可靠性,核心团队保留对项目技术方向与质量标准的把控 + +**提出新想法**。针对有路线图上还没有的想法,请新建 issue 描述提议。核心开发团队会尽可能地及时回复并一起讨论可行的推进路径。 + +## 2. 在贡献中负责任地使用 AI + +AgentScope 欢迎使用 AI 编码助手的贡献者——Claude Code、Cursor、Codex、Copilot 等等。我们只要求**负责任地使用**。AgentScope 依靠评审者的时间和社区信任运转,AI 辅助的贡献需要兼顾两者。 + +涉及 AI 时的几条要求: + +- **作者是人,不是 AI**。在 push 之前,请逐行阅读 diff,运行代码,确认理解了**改了什么**和**为什么改**。“Claude Code / Cursor / Codex 就是这么写的”并不是一个合适的理由,也不利于开源社区的健康发展。 + +- **创建 PR 前先自行评审 AI 生成的代码**。所有人的事件都是宝贵的资源,请不要将没有审阅过的 AI 代码/改动直接丢给维护者评审。 + +- **保持 PR 原子化**。不要提交 AI 一次性生成的 10K+ 行 PR,这种 PR 无法评审,会被拒绝。请把改动拆成若干个聚焦原子化功能的、具有单一目标的 PR。 + +- **AI 生成代码遵守同样的原则**。AgentScope 的所有开发原则——模块化、惰性导入、约定式提交、测试覆盖、不破坏 API——对 AI 辅助代码同等适用。 + +简而言之:AI 让我们的开发更快,但确保合入 AgentScope 的代码质量责任仍在贡献者本人。 + +## 3. 贡献流程 + +端到端的贡献流程如下。 + +### 第 1 步:认领或创建 issue + +在写代码之前,先找到或创建对应的 issue。 + +- **基于已有任务**:浏览 [Projects](https://github.com/orgs/agentscope-ai/projects/2) 与 [Issues](https://github.com/agentscope-ai/agentscope/issues) 中标有 `help wanted` 的条目(参见 [§1](#1-开发路线图与参与方式)),在 issue 下评论认领后再开始。 +- **提出新想法**:新建 issue 描述问题、方案与设计上的取舍。等待核心开发团队反馈后再开始实现,避免事后大规模返工。 + +### 第 2 步:Fork 仓库并创建开发分支 + +1. 在 GitHub 上 fork [agentscope-ai/agentscope](https://github.com/agentscope-ai/agentscope)。 +2. clone 自己的 fork 并添加 upstream 远端: + ```bash + git clone https://github.com//agentscope.git + cd agentscope + git remote add upstream https://github.com/agentscope-ai/agentscope.git + ``` +3. 基于最新的 `main` 创建主题分支: + ```bash + git checkout main + git pull upstream main + git checkout -b feat/ + ``` + +### 第 3 步:搭建本地环境 + +AgentScope 要求 **Python 3.11+**(详见 `pyproject.toml`)。 + +```bash +# 创建隔离环境(这里用 uv,也可用 virtualenv / conda) +uv venv +source .venv/bin/activate + +# 以可编辑模式安装 AgentScope,并带上 dev extras +pip install -e ".[dev]" +# 等价的 uv 写法: +uv pip install -e ".[dev]" + +# 启用 git pre-commit hooks +pre-commit install +``` + +`dev` extra 会拉入 `pre-commit`、`pytest`、文档工具链以及 `full` extra(包含 `models`、`service`、`storage`)。一次安装即可获得开发与运行完整测试套件所需的一切。 + +### 第 4 步:开发 + +写代码时遵守的几条约定: + +- **可选依赖必须惰性导入**。任何**未列在 `pyproject.toml` 的 `[project.dependencies]` 中**的依赖——也就是来自可选 extra(`gemini`、`ollama`、`xai`、`service`、`storage` 等)的——**必须在使用点惰性导入**,而不是放在模块顶部: + ```python + def some_function(): + import google.genai # 来自 `gemini` extra,惰性导入 + # ... 在这里使用 google.genai + ``` + 这样保持 `import agentscope` 轻量,`ImportError` 只在实际用到该 extra 的功能时才抛出。如果改动需要引入全新的依赖,先决定它属于基础 `[project.dependencies]`(始终需要、保持精简)还是某个可选 extra,并在 issue 中讨论后再合入。 + +- **遵守项目代码风格**。pre-commit 会自动处理格式与大部分 lint 规则,请在提交前运行 pre-commit 来修复问题。 + +- **功能要配套写单元测试**。测试位于 `tests/` 下,沿用现有结构。依赖可选 extra 的测试(如 Redis、Ollama)在该 extra 未安装时应能干净 skip。 + +### 第 5 步:跑 pre-commit、测试,并更新文档 + +创建 PR 之前,请在本地运行如下的命令检查代码格式与功能: + +```bash +# 自动格式化与 lint +pre-commit run --all-files + +# 单元测试 +pytest tests +``` + +如果 pre-commit hook 失败,请修复格式问题(多数会自动修复),然后重新 commit。 + +**改代码的同时请更新文档**。 + +- AgentScope 文档放在独立仓库:**[agentscope-ai/docs](https://github.com/agentscope-ai/docs)**。如果改动影响用户可见行为——新模块、新公开 API、行为变化、教程——请在该仓库同步开一个配套 PR。 +- 为新公开 API 更新 docstring 与示例片段。 +- 如果改动影响新手上手或 AgentScope 的对外宣传内容,更新 `README.md`。 + +### 第 6 步:提交与发起 PR + +**Commit 信息格式**。我们遵循 [Conventional Commits](https://www.conventionalcommits.org/) 规范,便于阅读历史与自动生成 changelog。 + +``` +(): +``` + +**Type 列表:** +- `feat:` 新功能 +- `fix:` bug 修复 +- `docs:` 仅文档变更 +- `style:` 不影响代码语义的改动(空白、格式等) +- `refactor:` 既不是修 bug 也不是加功能的代码改动 +- `perf:` 性能优化 +- `ci:` 增补或修正测试 +- `chore:` 构建流程或辅助工具/库的变更 + +**示例:** +```bash +feat(models): add support for Claude-3 model +fix(agent): resolve memory leak in ReActAgent +docs(readme): update installation instructions +refactor(formatter): simplify message formatting logic +ci(models): add unit tests for OpenAI integration +``` + +**PR 标题格式**。PR 标题同样遵循 Conventional Commits 格式,并由 GitHub Actions 在针对 `main` 的 PR 上自动校验。标题不合规的 PR 会被阻止合入,直到修正为止。 + +``` +(): +``` + +**要求:** +- 标题须以下列 type 之一开头:`feat`、`fix`、`docs`、`ci`、`refactor`、`test`、`chore`、`perf`、`style`、`build`、`revert` +- scope 可选,建议带上 +- **scope 必须小写**——只允许小写字母、数字、连字符(`-`)和下划线(`_`) +- description 以小写字母开头 +- 标题保持简洁、有信息量 + +**示例:** +``` +✅ 合规: +feat(memory): add redis cache support +fix(agent): resolve memory leak in ReActAgent +docs(tutorial): update installation guide +ci(workflow): add PR title validation +refactor(my-feature): simplify logic + +❌ 不合规: +feat(Memory): add cache # scope 必须小写 +feat(MEMORY): add cache # scope 必须小写 +feat(MyFeature): add feature # scope 必须小写 +``` + +**发起 PR**。把分支 push 到自己的 fork,对 `agentscope-ai/agentscope:main` 发起 pull request。在 PR 描述里: + +- 关联认领的 issue(`Fixes #123` 或 `Refs #123`) +- 概述改了什么、为什么改 +- 标注任何破坏性改动、废弃项或迁移步骤 +- 如果同时开了文档 PR,链接到 [agentscope-ai/docs](https://github.com/agentscope-ai/docs) 的对应 PR + +## 4. 重要事项 + +开始贡献前需要了解的几条横向约束。模块特定的事项见下文对应模块指南。 + +- **非平凡改动先开 issue**。突然提交涉及大量文件、改动公开 API 或引入新模块的 PR 难以评审,多半会被拒。先在 issue 中讨论设计。 +- **PR 保持聚焦、原子**。一个 PR 一个目的。不要把重构和功能、或功能和不相干的 bug 修复混在一起。 +- **不擅自破坏公开 API**。能保持向后兼容就保持。无法避免的破坏性改动,在 PR 描述中清楚说明,并在同一个 PR 里更新受影响的示例和文档。 +- **不绕过惰性导入原则**。可选依赖必须在使用点导入,不能放在模块顶部。 +- **不随意引入依赖**。每个新依赖都是长期维护负担。如果只有一个模块用到,优先在该模块内部惰性导入。 +- **不忽视 CI 失败**。pre-commit、类型检查、测试必须通过后再发起 review,不要把修复负担推给评审者。 +- **保持尊重**。遵守行为准则。AgentScope 的评审风格直接但友善,对贡献者也是同样期待。 + +## 5. 模块特定贡献指南 + +下文覆盖社区贡献者最常扩展的模块。其他模块请先开 issue 协调。 + +### Chat Model + +AgentScope 中的一个 chat model 不只是一个类——要在 `Agent` 中可用,需要一组上下游配套实现。一个完整的 chat model 贡献需包含**以下全部**: + +1. **Credential 类**——位于 `agentscope.credential`,继承 `CredentialBase`。承载 API key、endpoint 及 SDK 所需的其他鉴权字段。 + _参考:`agentscope/credential/_anthropic.py`_ + +2. **Chat model 类**——位于 `agentscope.model./`,继承 `ChatModelBase`。实现需覆盖: + - 流式与非流式两种模式 + - Tools API 集成(function/tool calling) + - `tool_choice` 参数 + - 适用时的 reasoning 模型支持 + + _参考:`agentscope/model/_anthropic/`_ + +3. **Model card YAML**——位于 `agentscope.model.._models/`,每个支持的模型一份 YAML。必填字段:`name`、`label`、`status`、`input_types`、`output_types`、`context_size`、`output_size`。可选字段:`parameter_overrides`、`deprecated_at`。 + + 示例(`claude-sonnet-4-6.yaml`): + ```yaml + name: claude-sonnet-4-6 + label: Claude Sonnet 4.6 + status: active + input_types: + - text/plain + - image/jpeg + output_types: + - text/plain + context_size: 1000000 + output_size: 65536 + parameter_overrides: + max_tokens: {"maximum": 65536} + ``` + +4. **Formatter 类**——位于 `agentscope.formatter`,均继承 `FormatterBase`。需要两种变体,因为部分 API 对多 agent 对话与单用户对话的处理方式不同: + - `ChatFormatter` 处理单用户对话场景 + - `MultiAgentFormatter` 处理多 agent 场景 + + 每个 formatter 把 `Msg` 对象转换成对应 provider API 期望的请求格式。 + _参考:`agentscope/formatter/_anthropic_formatter.py`_ + +> ⚠️ 只加 model 类、缺少配套 credential、model card YAML 与两种 formatter 变体的 PR 不会被合入。 + +### Agent + +AgentScope 目前只维护**一个核心 agent 类**——`agentscope.agent.Agent`——它整合了 AgentScope 库的全部功能(memory、tools、MCP、formatter、model 等)。 + +特定领域或专用 agent 请作为 [example](#examples) 贡献,而不是在 `agentscope.agent` 中新增类。 + +如果确信某个用例需要新的顶层 agent 类: + +1. **先开 issue**,描述用例并说明为什么组合现有 `Agent` 能力不够。 +2. **等核心团队的设计讨论**,再开始具体的代码实现。 +3. 未经事先讨论就引入新 agent 类的 PR 会被拒绝。 + +### Workspace + +Workspace 提供 agent 运行所需的运行时上下文(skills、scheduled tasks 等)。新增 workspace 后端需要两个类加配套文档: + +1. **Workspace 类**——位于 `agentscope.workspace`,继承 `WorkspaceBase`。实现该后端的存储与生命周期语义。 + _参考:`agentscope/workspace/_local_workspace.py`(`LocalWorkspace`)_ + +2. **Workspace manager 类**——位于 `agentscope/app/_manager/_workspace_manager.py`,继承 `WorkspaceManagerBase`。把 workspace 接入应用生命周期。 + _参考:同文件中的 `LocalWorkspaceManager`_ + +3. **文档**——在 [agentscope-ai/docs](https://github.com/agentscope-ai/docs) 配套发起 PR,说明该 workspace 的配置与使用方式。 + +### Examples + +我们非常欢迎新增展示 AgentScope 能力的 example。 + +主仓库 `examples/` 目录聚焦于**演示具体特性与能力**——简洁、教学性的参考实现。更完整、贴近生产形态的应用,请贡献到 **[agentscope-samples](https://github.com/agentscope-ai/agentscope-samples)**。 + +新 example 放在自己的子目录下: + +``` +examples/ +└── / + ├── main.py + ├── README.md # 说明 example 的目的、运行方式与预期输出 + └── ... +``` + +`examples/agent_service/` 是不错的参考起点。 + +## 获取帮助 + +需要协助或有问题,可以: + +- 发起 [Discussion](https://github.com/agentscope-ai/agentscope/discussions) +- 在 [Issues](https://github.com/agentscope-ai/agentscope/issues) 中报告 bug +- 通过钉钉或 Discord 联系维护者(链接见 README.md) + + +--- + +感谢您为 AgentScope 所做的贡献!每一份努力都在为社区构建更好的开源工具。 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2f2f2f2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,391 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Alibaba + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +-------------------------------------------------------------------------------- + + +Some codes of tests/run.py is modified from +https://github.com/alibaba/FederatedScope/blob/master/tests/run.py, which is +also licensed under the terms of the Apache 2.0. + + +-------------------------------------------------------------------------------- + +Code in src/agentscope/web/static/js/socket.io.js is adapted from +https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.js (MIT License) + +Copyright (c) 2014-2021 Guillermo Rauch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- + +Code in src/agentscope/web/static/js/jquery-3.3.1.min.js is adapted from +https://code.jquery.com/jquery-3.3.1.min.js (MIT License) + +Copyright (c) JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- + +Code in src/agentscope/web/static/js/bootstrap.bundle.min.js is adapted from +https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/js/bootstrap.bundle.min.js + (MIT License) + +Copyright (c) 2011-2019 The Bootstrap Authors (https://github +.com/twbs/bootstrap/graphs/contributors) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- + +Code in src/agentscope/web/static/js/bootstrap-table.min.js is adapted from +https://unpkg.com/bootstrap-table@1.18.0/dist/bootstrap-table.min.js (MIT +License) + +Copyright (c) wenzhixin (http://wenzhixin.net.cn/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- + +Code in src/agentscope/web/static/css/bootstrap.min.css is adapted from +https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css (MIT +License) + +Copyright 2011-2019 The Bootstrap Authors +Copyright 2011-2019 Twitter, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- + +Fonts in src/agentscope/web/static/fonts/KRYPTON.ttf is adapted from +https://github.com/githubnext/monaspace (SIL Open Font License 1.1). These +fonts are distributed with their original license. See https://github +.com/githubnext/monaspace/blob/main/LICENSE for the full text of the license. +The following font families are included: + +- Monaspace (with subfamilies: Krypton) + +Copyright (c) 2023, GitHub https://github.com/githubnext/monaspace +with Reserved Font Name "Monaspace", including subfamilies: "Argon", "Neon", +"Xenon", "Radon", and "Krypton" + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +-------------------------------------------------------------------------------- + +Fonts in src/agentscope/web/static/fonts/OSWALD.ttf is adapted from +https://fonts.google.com/specimen/Oswald (SIL Open Font License 1.1). These +fonts are distributed with their original license. See https://github +.com/googlefonts/OswaldFont/blob/main/OFL.txt for the full text of the license. + +Copyright 2016 The Oswald Project Authors (https://github +.com/googlefonts/OswaldFont) + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +-------------------------------------------------------------------------------- \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c721dc --- /dev/null +++ b/README.md @@ -0,0 +1,277 @@ +

+ AgentScope Logo +

+ + + +[**中文主页**](https://github.com/agentscope-ai/agentscope/blob/main/README_zh.md) | [**Documentation**](https://docs.agentscope.io/) | [**Roadmap**](https://github.com/orgs/agentscope-ai/projects/2/views/1) + + + +

+ + arxiv + + + pypi + + + pypi + + + discord + + + docs + + + license + +

+ +

+agentscope-ai%2Fagentscope | Trendshift +

+ +## What is AgentScope 2.0? + +AgentScope 2.0 is a production-ready, easy-to-use agent framework with essential abstractions that work with rising model capability and built-in support for . + +- [**Event System** →](https://docs.agentscope.io/latest/en/building-blocks/message-and-event) A unified event bus to the frontend and human-in-the-loop support. +- [**Permission System** →](https://docs.agentscope.io/latest/en/building-blocks/permission-system) Fine-grained, configurable control over tools and resources. +- [**Multi-tenancy & Multi-session Service** →](https://docs.agentscope.io/latest/en/deploy/agent-service) Production-grade serving with isolation across tenants and sessions. +- [**Workspace / Sandbox Support** →](https://docs.agentscope.io/latest/en/building-blocks/workspace) Run tools and code in isolated environments, with built-in backends for local, Docker, E2B, and OpenSandbox. +- [**Extensible Middleware System** →](https://docs.agentscope.io/latest/en/building-blocks/middleware) Composable hooks to customize and extend the agent's reasoning-acting loop. + +We design for increasingly agentic LLMs. +Our approach leverages the models' reasoning and tool use abilities +rather than constraining them with strict prompts and opinionated orchestrations. + +agentscope + +## News + +- **[2026-07] `INTE`:** K8s, OpenSandbox-based workspace/sandbox supported. [Docs](https://docs.agentscope.io/latest/en/building-blocks/workspace) +- **[2026-07] `INTE`:** ReMe long-term memory supported. [Example](https://github.com/agentscope-ai/agentscope/tree/main/examples/long_term_memory/reme) | [Docs](https://docs.agentscope.io/latest/en/building-blocks/long-term-memory) +- **[2026-06] `FEAT`:** Agentic Memory supported. [Example](https://github.com/agentscope-ai/agentscope/tree/main/examples/long_term_memory/agentic_memory) | [Docs](https://docs.agentscope.io/latest/en/building-blocks/long-term-memory) +- **[2026-06] `FEAT`:** Distributed & Multi-Tenancy & Multi-Session RAG service supported. [Docs](https://docs.agentscope.io/latest/en/deploy/agent-team) +- **[2026-06] `FEAT`:** RAG supported. [Example](https://github.com/agentscope-ai/agentscope/tree/main/examples/rag) | [Docs](https://docs.agentscope.io/latest/en/building-blocks/rag) +- **[2026-06] `INTE`:** Mem0 supported. [Example](https://github.com/agentscope-ai/agentscope/tree/main/examples/long_term_memory) | [Docs](https://docs.agentscope.io/latest/en) +- **[2026-06] `FEAT`:** Agent Team supported. [Example](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent_service) | [Docs](https://docs.agentscope.io/latest/en/deploy/agent-team) +- **[2026-05] `RELS`:** AgentScope 2.0 released! [Docs](https://docs.agentscope.io/) + + +[More news →](./docs/NEWS.md) + +## Community + +Welcome to join our community on + +| [Discord](https://discord.gg/eYMpfnkG8h) | DingTalk | +|----------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------| +| | | + +## Quickstart + +### Installation + +> AgentScope requires **Python 3.11** or higher. + +#### From PyPI + +```bash +uv pip install agentscope +# or +# pip install agentscope +``` + +#### From source + +```bash +# Pull the source code from GitHub +git clone -b main https://github.com/agentscope-ai/agentscope.git + +# Install the package in editable mode +cd agentscope + +uv pip install -e . +# or +# pip install -e . +``` + +## Hello AgentScope! + +Start your first agent with AgentScope 2.0: + +```python +from agentscope.agent import Agent +from agentscope.tool import Toolkit, Bash, Grep, Glob, Read, Write, Edit +from agentscope.credential import DashScopeCredential +from agentscope.model import DashScopeChatModel +from agentscope.message import UserMsg +from agentscope.event import EventType + +import os, asyncio + + +async def main() -> None: + agent = Agent( + name="Friday", + system_prompt="You're a helpful assistant named Friday.", + model=DashScopeChatModel( + credential=DashScopeCredential( + api_key=os.environ["DASHSCOPE_API_KEY"] + ), + model="qwen3.6-plus", + ), + toolkit=Toolkit( + tools=[ + Bash(), + Grep(), + Glob(), + Read(), + Write(), + Edit(), + ] + ), + ) + + async for evt in agent.reply_stream(UserMsg("Tony", "Hi, Friday!")): + # Handle the event stream, e.g., print the message, update UI, etc. + match evt.type: + case EventType.REPLY_START: + ... + case EventType.MODEL_CALL_START: + ... + case EventType.TEXT_BLOCK_START: + ... + case EventType.TEXT_BLOCK_DELTA: + ... + case EventType.TEXT_BLOCK_END: + ... + + # Handle other event types + +asyncio.run(main()) +``` + +## Hello Agent Service! + +An extensible FastAPI based **multi-tenancy**, **multi-session** agent service with pre-built Web UI in `examples/web_ui` + + + + + + + + + + + + + + +
+ Agent team +
+ Agent team — a leader agent spawns workers and coordinates them through the built-in team tools. +
+ Task planning +
+ Task planning — the agent breaks complex work into a tracked plan and updates it as it goes. +
+ Permission control in bypass mode +
+ Permission control in bypass mode — the agent runs end-to-end without pausing for tool-call confirmations. +
+ Background task offloading +
+ Background task offloading — a long-running tool moves to the background; its result later wakes the agent up and the conversation resumes. +
+ +Run the following commands to start the agent service backend and the web UI: + +```bash +git clone -b main https://github.com/agentscope-ai/agentscope.git +cd agentscope/examples/agent_service + +# start the agent service backend +python main.py +``` + +Then open another terminal to start the web UI: + +```bash +cd agentscope/examples/web_ui + +# start the webui +pnpm install +pnpm dev +``` + + +## Contributing + +We welcome contributions from the community! Please refer to our [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines +on how to contribute. + +## License + +AgentScope is released under Apache License 2.0. + +## Publications + +If you find our work helpful for your research or application, please cite our papers. + +- [AgentScope 1.0: A Developer-Centric Framework for Building Agentic Applications](https://arxiv.org/abs/2508.16279) + +- [AgentScope: A Flexible yet Robust Multi-Agent Platform](https://arxiv.org/abs/2402.14034) + +``` +@article{agentscope_v1, + author = {Dawei Gao, Zitao Li, Yuexiang Xie, Weirui Kuang, Liuyi Yao, Bingchen Qian, Zhijian Ma, Yue Cui, Haohao Luo, Shen Li, Lu Yi, Yi Yu, Shiqi He, Zhiling Luo, Wenmeng Zhou, Zhicheng Zhang, Xuguang He, Ziqian Chen, Weikai Liao, Farruh Isakulovich Kushnazarov, Yaliang Li, Bolin Ding, Jingren Zhou} + title = {AgentScope 1.0: A Developer-Centric Framework for Building Agentic Applications}, + journal = {CoRR}, + volume = {abs/2508.16279}, + year = {2025}, +} + +@article{agentscope, + author = {Dawei Gao, Zitao Li, Xuchen Pan, Weirui Kuang, Zhijian Ma, Bingchen Qian, Fei Wei, Wenhao Zhang, Yuexiang Xie, Daoyuan Chen, Liuyi Yao, Hongyi Peng, Zeyu Zhang, Lin Zhu, Chen Cheng, Hongzhu Shi, Yaliang Li, Bolin Ding, Jingren Zhou} + title = {AgentScope: A Flexible yet Robust Multi-Agent Platform}, + journal = {CoRR}, + volume = {abs/2402.14034}, + year = {2024}, +} +``` + +## Contributors + +All thanks to our contributors: + + + + diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..b955ac6 --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`agentscope-ai/agentscope` +- 原始仓库:https://github.com/agentscope-ai/agentscope +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 0000000..081cac7 --- /dev/null +++ b/README_zh.md @@ -0,0 +1,300 @@ +

+ AgentScope Logo +

+ + + +[**English Homepage**](https://github.com/agentscope-ai/agentscope/blob/main/README.md) | [**文档**](https://docs.agentscope.io/) | [**路线图**](https://github.com/orgs/agentscope-ai/projects/2/views/1) + + + +

+ + arxiv + + + pypi + + + pypi + + + discord + + + docs + + + license + +

+ +

+agentscope-ai%2Fagentscope | Trendshift +

+ +## 什么是 AgentScope 2.0? + +AgentScope 2.0 是一款面向生产、易于使用的智能体框架,提供与不断进化的模型能力相匹配的核心抽象。 + +- [**事件系统** →](https://docs.agentscope.io/latest/zh/building-blocks/message-and-event) 统一的事件总线,服务于前端智能体应用与 human-in-the-loop 协作。 +- [**权限系统** →](https://docs.agentscope.io/latest/zh/building-blocks/permission-system) 对工具和资源进行细粒度、可配置的控制。 +- [**多租户与多会话服务** →](https://docs.agentscope.io/latest/zh/deploy/agent-service) 提供生产级服务,在租户与会话之间实现隔离。 +- [**工作区 / 沙箱支持** →](https://docs.agentscope.io/latest/zh/building-blocks/workspace) 在隔离环境中运行工具和代码,内置支持本地文件系统、Docker、E2B 和 OpenSandbox 后端。 +- [**可扩展中间件系统** →](https://docs.agentscope.io/latest/zh/building-blocks/middleware) 可组合的钩子系统,用于自定义和扩展智能体的推理-行动循环。 + +我们为日益自主的大语言模型而设计。 +我们的方法是充分发挥模型的推理与工具调用能力, +而不是用严格的提示词和固化的编排方式来束缚它们。 + +## 为什么选择 AgentScope? + +- **简单**:通过内置的 ReAct 智能体、工具、技能、人机协作干预、记忆、计划、实时语音、评估和模型微调,5 分钟即可开始构建你的智能体 +- **可扩展**:丰富的生态系统集成,覆盖工具、记忆和可观测性;内置 MCP 和 A2A 支持;通过消息中心(MsgHub)实现灵活的多智能体编排和工作流 +- **生产就绪**:支持本地部署、云端 Serverless 部署或 K8s 集群部署,并内置 OTel 支持 + +agentscope + +## 新闻 + +- **[2026-07] `集成`:** 集成 K8s,OpenSandbox 工作区/沙箱实现。 [文档](https://docs.agentscope.io/latest/en/building-blocks/workspace) +- **[2026-07] `集成`:** 集成 ReMe 长期记忆。 [样例](https://github.com/agentscope-ai/agentscope/tree/main/examples/long_term_memory/reme) | [文档](https://docs.agentscope.io/latest/zh/building-blocks/long-term-memory) +- **[2026-06] `功能`:** 支持 Agentic Memory。 [样例](https://github.com/agentscope-ai/agentscope/tree/main/examples/long_term_memory/agentic_memory) | [文档](https://docs.agentscope.io/latest/zh/building-blocks/long-term-memory) +- **[2026-06] `功能`:** 支持分布式 & 多租户 & 多会话 RAG 服务。 [文档](https://docs.agentscope.io/latest/en/deploy/agent-team) +- **[2026-06] `功能`:** 支持多模态 RAG。 [样例](https://github.com/agentscope-ai/agentscope/tree/main/examples/rag) | [文档](https://docs.agentscope.io/latest/en/building-blocks/rag) +- **[2026-06] `集成`:** 集成 Mem0 长期记忆。 [Example](https://github.com/agentscope-ai/agentscope/tree/main/examples/long_term_memory) | [Docs](https://docs.agentscope.io/latest/zh) +- **[2026-06] `功能`:** 支持 Agent Team。[样例](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent_service) | [文档](https://docs.agentscope.io/latest/zh/deploy/agent-team) +- **[2026-05] `发布`:** AgentScope 2.0 已发布![文档](https://docs.agentscope.io/) + + +[更多新闻 →](./docs/NEWS_zh.md) + +## 社区 + +欢迎加入我们的社区 + +| [Discord](https://discord.gg/eYMpfnkG8h) | 钉钉 | +|----------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------| +| | | + + + +## 📑 Table of Contents + +- [快速开始](#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B) + - [安装](#%E5%AE%89%E8%A3%85) + - [从 PyPI 安装](#%E4%BB%8E-pypi-%E5%AE%89%E8%A3%85) + - [从源码安装](#%E4%BB%8E%E6%BA%90%E7%A0%81%E5%AE%89%E8%A3%85) +- [Hello AgentScope!](#hello-agentscope) +- [智能体服务](#%E6%99%BA%E8%83%BD%E4%BD%93%E6%9C%8D%E5%8A%A1) +- [贡献](#%E8%B4%A1%E7%8C%AE) +- [许可](#%E8%AE%B8%E5%8F%AF) +- [论文](#%E8%AE%BA%E6%96%87) +- [贡献者](#%E8%B4%A1%E7%8C%AE%E8%80%85) + + + +## 快速开始 + +### 安装 + +> AgentScope 需要 **Python 3.11** 或更高版本。 + +#### 从 PyPI 安装 + +```bash +uv pip install agentscope +# 或者 +# pip install agentscope +``` + +#### 从源码安装 + +```bash +# 从 GitHub 拉取源码 +git clone -b main https://github.com/agentscope-ai/agentscope.git + +# 以可编辑模式安装 +cd agentscope + +uv pip install -e . +# 或者 +# pip install -e . +``` + +## Hello AgentScope! + +使用 AgentScope 2.0,启动你的第一个智能体: + +```python +from agentscope.agent import Agent +from agentscope.tool import Toolkit, Bash, Grep, Glob, Read, Write, Edit +from agentscope.credential import DashScopeCredential +from agentscope.model import DashScopeChatModel +from agentscope.message import UserMsg +from agentscope.event import EventType + +import os, asyncio + + +async def main() -> None: + agent = Agent( + name="Friday", + system_prompt="You're a helpful assistant named Friday.", + model=DashScopeChatModel( + credential=DashScopeCredential( + api_key=os.environ["DASHSCOPE_API_KEY"] + ), + model="qwen3.6-plus", + ), + toolkit=Toolkit( + tools=[ + Bash(), + Grep(), + Glob(), + Read(), + Write(), + Edit(), + ] + ), + ) + + async for evt in agent.reply_stream(UserMsg("Tony", "Hi, Friday!")): + # 处理事件流,例如打印消息、更新 UI 等 + match evt.type: + case EventType.REPLY_START: + ... + case EventType.MODEL_CALL_START: + ... + case EventType.TEXT_BLOCK_START: + ... + case EventType.TEXT_BLOCK_DELTA: + ... + case EventType.TEXT_BLOCK_END: + ... + + # 处理其他事件类型 + +asyncio.run(main()) +``` + +## 智能体服务 + +一个基于 FastAPI 的可扩展**多租户**、**多会话**智能体服务,并在 `examples/web_ui` 中提供预构建的 Web UI + + + + + + + + + + + + + + +
+ 智能体团队 +
+ 智能体团队 —— leader 智能体派生 worker,并通过内置的团队工具进行协调。 +
+ 任务规划 +
+ 任务规划 —— 智能体将复杂工作拆解为可追踪的计划,并在执行过程中持续更新。 +
+ bypass 模式下的权限控制 +
+ bypass 模式下的权限控制 —— 智能体端到端运行,无需为工具调用确认而暂停。 +
+ 后台任务卸载 +
+ 工具后台执行 —— 长时间运行的工具被转入后台;其结果稍后唤醒智能体并恢复对话。 +
+ +运行以下命令启动智能体服务后端和 Web UI: + +```bash +git clone -b main https://github.com/agentscope-ai/agentscope.git +cd agentscope/examples/agent_service + +# 启动智能体服务后端 +python main.py +``` + +然后打开另一个终端启动 Web UI: + +```bash +cd agentscope/examples/web_ui + +# 启动 webui +pnpm install +pnpm dev +``` + + + +## 贡献 + +我们欢迎社区的贡献!请参阅我们的 [贡献指南](./CONTRIBUTING_zh.md) 了解如何贡献。 + +## 许可 + +AgentScope 基于 Apache License 2.0 发布。 + +## 论文 + +如果我们的工作对您的研究或应用有帮助,请引用我们的论文。 + +- [AgentScope 1.0: A Developer-Centric Framework for Building Agentic Applications](https://arxiv.org/abs/2508.16279) + +- [AgentScope: A Flexible yet Robust Multi-Agent Platform](https://arxiv.org/abs/2402.14034) + +``` +@article{agentscope_v1, + author = {Dawei Gao, Zitao Li, Yuexiang Xie, Weirui Kuang, Liuyi Yao, Bingchen Qian, Zhijian Ma, Yue Cui, Haohao Luo, Shen Li, Lu Yi, Yi Yu, Shiqi He, Zhiling Luo, Wenmeng Zhou, Zhicheng Zhang, Xuguang He, Ziqian Chen, Weikai Liao, Farruh Isakulovich Kushnazarov, Yaliang Li, Bolin Ding, Jingren Zhou} + title = {AgentScope 1.0: A Developer-Centric Framework for Building Agentic Applications}, + journal = {CoRR}, + volume = {abs/2508.16279}, + year = {2025}, +} + +@article{agentscope, + author = {Dawei Gao, Zitao Li, Xuchen Pan, Weirui Kuang, Zhijian Ma, Bingchen Qian, Fei Wei, Wenhao Zhang, Yuexiang Xie, Daoyuan Chen, Liuyi Yao, Hongyi Peng, Zeyu Zhang, Lin Zhu, Chen Cheng, Hongzhu Shi, Yaliang Li, Bolin Ding, Jingren Zhou} + title = {AgentScope: A Flexible yet Robust Multi-Agent Platform}, + journal = {CoRR}, + volume = {abs/2402.14034}, + year = {2024}, +} +``` + +## 贡献者 + +感谢所有贡献者: + + + + diff --git a/assets/images/agentscope.png b/assets/images/agentscope.png new file mode 100644 index 0000000000000000000000000000000000000000..0c02c6487788576d0e111f651f6121f11f1b518c GIT binary patch literal 652417 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

@j#HvtEQ2`Gk45xopC8FF!;{^k4m{{G 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