From 5defe661fadf9dae766c7d7e3012f83e68afb70e Mon Sep 17 00:00:00 2001 From: wehub-resource-sync Date: Mon, 13 Jul 2026 12:45:58 +0800 Subject: [PATCH] chore: import upstream snapshot with attribution --- .env.sample | 50 + .git-blame-ignore-revs | 2 + .github/workflows/black.yml | 10 + .github/workflows/release.yml | 154 + .github/workflows/run_pytest.yml | 24 + .gitignore | 71 + .pre-commit-config.yaml | 11 + CONTRIBUTING.md | 213 + LICENSE | 18 + README-alternative.md | 132 + README.md | 219 + README.wehub.md | 7 + aisuite-js/README.md | 304 + aisuite-js/examples/.env.example | 5 + aisuite-js/examples/basic-usage.ts | 66 + aisuite-js/examples/chat-app/.eslintrc.cjs | 18 + aisuite-js/examples/chat-app/.gitignore | 31 + aisuite-js/examples/chat-app/README.md | 210 + aisuite-js/examples/chat-app/index.html | 13 + aisuite-js/examples/chat-app/package.json | 34 + .../examples/chat-app/postcss.config.js | 6 + aisuite-js/examples/chat-app/src/App.tsx | 333 + .../chat-app/src/components/ApiKeyModal.tsx | 169 + .../chat-app/src/components/ChatContainer.tsx | 75 + .../chat-app/src/components/ChatInput.tsx | 79 + .../chat-app/src/components/ChatMessage.tsx | 51 + .../chat-app/src/components/ModelSelector.tsx | 38 + .../src/components/ProviderSelector.tsx | 51 + .../chat-app/src/config/llm-config.ts | 57 + aisuite-js/examples/chat-app/src/index.css | 87 + aisuite-js/examples/chat-app/src/main.tsx | 10 + .../chat-app/src/services/aisuite-service.ts | 62 + .../examples/chat-app/src/types/chat.ts | 49 + aisuite-js/examples/chat-app/src/utils/cn.ts | 6 + .../examples/chat-app/tailwind.config.js | 59 + aisuite-js/examples/chat-app/tsconfig.json | 25 + .../examples/chat-app/tsconfig.node.json | 10 + aisuite-js/examples/chat-app/vite.config.ts | 11 + aisuite-js/examples/deepgram.ts | 73 + aisuite-js/examples/groq.ts | 215 + aisuite-js/examples/mistral.ts | 240 + aisuite-js/examples/openai-asr.ts | 68 + aisuite-js/examples/streaming.ts | 212 + aisuite-js/examples/test-suite.ts | 294 + aisuite-js/examples/tool-calling.ts | 164 + aisuite-js/jest.config.ts | 33 + aisuite-js/package.json | 72 + .../src/asr-providers/deepgram/adapters.ts | 53 + .../src/asr-providers/deepgram/index.ts | 2 + .../src/asr-providers/deepgram/provider.ts | 118 + .../src/asr-providers/deepgram/types.ts | 4 + aisuite-js/src/asr-providers/index.ts | 2 + aisuite-js/src/client.ts | 137 + aisuite-js/src/core/base-asr-provider.ts | 22 + aisuite-js/src/core/base-provider.ts | 20 + aisuite-js/src/core/errors.ts | 57 + aisuite-js/src/core/model-parser.ts | 23 + aisuite-js/src/index.ts | 14 + .../src/providers/anthropic/adapters.ts | 187 + aisuite-js/src/providers/anthropic/index.ts | 2 + .../src/providers/anthropic/provider.ts | 97 + aisuite-js/src/providers/anthropic/types.ts | 10 + aisuite-js/src/providers/groq/adapters.ts | 57 + aisuite-js/src/providers/groq/index.ts | 2 + aisuite-js/src/providers/groq/provider.ts | 93 + aisuite-js/src/providers/groq/types.ts | 5 + aisuite-js/src/providers/index.ts | 10 + aisuite-js/src/providers/mistral/adapters.ts | 85 + aisuite-js/src/providers/mistral/index.ts | 2 + aisuite-js/src/providers/mistral/provider.ts | 90 + aisuite-js/src/providers/mistral/types.ts | 9 + aisuite-js/src/providers/openai/adapters.ts | 152 + aisuite-js/src/providers/openai/index.ts | 2 + aisuite-js/src/providers/openai/provider.ts | 145 + aisuite-js/src/providers/openai/types.ts | 45 + aisuite-js/src/types/chat.ts | 64 + aisuite-js/src/types/common.ts | 8 + aisuite-js/src/types/index.ts | 5 + aisuite-js/src/types/providers.ts | 33 + aisuite-js/src/types/tools.ts | 28 + aisuite-js/src/types/transcription.ts | 34 + aisuite-js/src/utils/streaming.ts | 29 + aisuite-js/tests/client.test.ts | 663 ++ .../providers/anthropic-provider.test.ts | 427 + .../tests/providers/deepgram-provider.test.ts | 594 ++ .../tests/providers/groq-provider.test.ts | 422 + .../tests/providers/mistral-provider.test.ts | 397 + .../tests/providers/openai-provider.test.ts | 365 + .../providers/openai_asr_provider.test.ts | 86 + aisuite-js/tests/utils/streaming.test.ts | 242 + aisuite-js/tsconfig.json | 23 + aisuite/__init__.py | 71 + aisuite/agents/__init__.py | 64 + aisuite/agents/artifact_store.py | 207 + aisuite/agents/artifacts.py | 218 + aisuite/agents/context.py | 45 + aisuite/agents/policies.py | 61 + aisuite/agents/postgres_state_store.py | 614 ++ aisuite/agents/runner.py | 590 ++ aisuite/agents/state_store.py | 182 + aisuite/agents/tools.py | 48 + aisuite/agents/types.py | 291 + aisuite/agents/utils.py | 85 + aisuite/agents/viewer.py | 4 + aisuite/client.py | 722 ++ .../asr-parameter-design-motivation.md | 155 + aisuite/framework/__init__.py | 3 + aisuite/framework/asr_params.py | 296 + aisuite/framework/chat_completion_response.py | 16 + aisuite/framework/choice.py | 15 + aisuite/framework/message.py | 298 + aisuite/framework/parameter_mapper.py | 224 + aisuite/framework/provider_interface.py | 26 + aisuite/mcp/__init__.py | 33 + aisuite/mcp/client.py | 788 ++ aisuite/mcp/config.py | 257 + aisuite/mcp/schema_converter.py | 198 + aisuite/mcp/tool_wrapper.py | 152 + aisuite/provider.py | 110 + aisuite/providers/__init__.py | 0 aisuite/providers/anthropic_provider.py | 242 + aisuite/providers/aws_provider.py | 291 + aisuite/providers/azure_provider.py | 136 + aisuite/providers/cerebras_provider.py | 44 + aisuite/providers/cohere_provider.py | 163 + aisuite/providers/deepgram_provider.py | 423 + aisuite/providers/deepseek_provider.py | 49 + aisuite/providers/fireworks_provider.py | 141 + aisuite/providers/google_provider.py | 567 ++ aisuite/providers/groq_provider.py | 62 + aisuite/providers/huggingface_provider.py | 294 + aisuite/providers/inception_provider.py | 34 + aisuite/providers/lmstudio_provider.py | 29 + aisuite/providers/message_converter.py | 80 + aisuite/providers/mistral_provider.py | 74 + aisuite/providers/nebius_provider.py | 30 + aisuite/providers/ollama_provider.py | 42 + aisuite/providers/openai_provider.py | 248 + aisuite/providers/openrouter_provider.py | 49 + aisuite/providers/sambanova_provider.py | 53 + aisuite/providers/together_provider.py | 65 + aisuite/providers/watsonx_provider.py | 39 + aisuite/providers/xai_provider.py | 66 + aisuite/toolkits/__init__.py | 5 + aisuite/toolkits/files.py | 809 ++ aisuite/toolkits/git.py | 96 + aisuite/toolkits/shell.py | 166 + aisuite/tracing/__init__.py | 31 + aisuite/tracing/normalize.py | 227 + aisuite/tracing/sinks.py | 157 + .../static/viewer/assets/index-C29aAR1d.js | 10 + .../static/viewer/assets/index-C2bMCWqj.css | 1 + aisuite/tracing/static/viewer/index.html | 13 + aisuite/tracing/store.py | 241 + aisuite/tracing/viewer.py | 1678 ++++ aisuite/utils/tools.py | 667 ++ aisuite/utils/utils.py | 61 + cli/py/aisuite-code-cli/README.md | 47 + cli/py/aisuite-code-cli/TRY_IT.md | 49 + .../aisuite_code_cli/__init__.py | 6 + .../aisuite_code_cli/agent.py | 92 + .../aisuite-code-cli/aisuite_code_cli/app.py | 280 + .../aisuite_code_cli/approval.py | 212 + .../aisuite_code_cli/config.py | 101 + .../aisuite-code-cli/aisuite_code_cli/main.py | 15 + .../aisuite_code_cli/rendering.py | 146 + cli/py/aisuite-code-cli/poetry.lock | 720 ++ cli/py/aisuite-code-cli/pyproject.toml | 22 + docs/agents-quickstart.md | 139 + docs/chat-completions-quickstart.md | 71 + docs/opencoworker-quickstart.md | 48 + examples/AISuiteDemo.ipynb | 637 ++ examples/DeepseekPost.ipynb | 202 + examples/QnA_with_pdf.ipynb | 230 + examples/agents/agent_api_quickstart.ipynb | 161 + .../agents/local_observability_demo.ipynb | 251 + examples/agents/movie_buff_assistant.ipynb | 484 ++ examples/agents/recipe_chef_assistant.ipynb | 608 ++ examples/agents/simple_agent.py | 55 + examples/agents/snake_game_generator.ipynb | 237 + examples/agents/stock_dashboard.html | 179 + examples/agents/stock_market_dashboard.html | 139 + .../agents/stock_market_mini_tracker.ipynb | 281 + examples/agents/stock_market_tracker.ipynb | 501 ++ examples/agents/world_weather_dashboard.ipynb | 242 + examples/aisuite_tool_abstraction.ipynb | 194 + examples/asr_example.ipynb | 132 + examples/chat-ui/.streamlit/config.toml | 7 + examples/chat-ui/README.md | 36 + examples/chat-ui/chat.py | 252 + examples/chat-ui/config.yaml | 14 + examples/cli/create_demo_trace.py | 288 + examples/cli/dev.py | 379 + examples/client.ipynb | 282 + examples/llm_reasoning.ipynb | 314 + examples/mcp_config_dict_example.py | 198 + examples/mcp_http_example.py | 270 + examples/mcp_tools_example.ipynb | 379 + examples/simple_tool_calling.ipynb | 298 + examples/tool_calling_abstraction.ipynb | 425 + guides/README.md | 25 + guides/anthropic.md | 47 + guides/aws.md | 71 + guides/azure.md | 63 + guides/cerebras.md | 54 + guides/cohere.md | 44 + guides/deepseek.md | 46 + guides/google.md | 92 + guides/groq.md | 39 + guides/huggingface.md | 57 + guides/lmstudio.md | 77 + guides/mistral.md | 49 + guides/nebius.md | 44 + guides/ollama.md | 50 + guides/openai.md | 44 + guides/sambanova.md | 44 + guides/watsonx.md | 83 + guides/xai.md | 33 + platform/.gitignore | 8 + platform/coworker/__init__.py | 3 + platform/coworker/agent.py | 322 + platform/coworker/agents/__init__.py | 17 + platform/coworker/agents/base.py | 44 + platform/coworker/agents/chat.py | 22 + platform/coworker/agents/code.py | 74 + platform/coworker/agents/cowork.py | 54 + platform/coworker/agents/myhelper.py | 39 + platform/coworker/agents/registry.py | 28 + platform/coworker/attachments.py | 82 + platform/coworker/audit.py | 174 + platform/coworker/automation/__init__.py | 18 + platform/coworker/automation/models.py | 155 + platform/coworker/automation/scheduler.py | 97 + platform/coworker/automation/store.py | 161 + platform/coworker/automation/tools.py | 195 + platform/coworker/catalog.py | 177 + platform/coworker/cli.py | 70 + platform/coworker/config.py | 97 + platform/coworker/connections.py | 181 + platform/coworker/connectors/__init__.py | 71 + platform/coworker/connectors/adapters.py | 307 + platform/coworker/connectors/base.py | 174 + .../coworker/connectors/browser_automation.py | 572 ++ platform/coworker/connectors/cli.py | 110 + platform/coworker/connectors/config.py | 65 + platform/coworker/connectors/descriptors.py | 867 ++ platform/coworker/connectors/email_tools.py | 840 ++ .../connectors/experimental/__init__.py | 18 + platform/coworker/connectors/fake.py | 57 + platform/coworker/connectors/gateway.py | 170 + .../coworker/connectors/integration_tools.py | 1988 +++++ platform/coworker/connectors/senders.py | 133 + platform/coworker/connectors/setup.py | 154 + platform/coworker/connectors/tool_defs.py | 515 ++ platform/coworker/connectors/tools.py | 78 + platform/coworker/conversations.py | 350 + platform/coworker/engine.py | 692 ++ platform/coworker/environment.py | 72 + platform/coworker/events.py | 36 + platform/coworker/inbox.py | 244 + platform/coworker/inbox_routing.py | 130 + platform/coworker/interactions.py | 54 + platform/coworker/mcp/__init__.py | 29 + platform/coworker/mcp/client.py | 132 + platform/coworker/mcp/config.py | 125 + platform/coworker/mcp/tools.py | 91 + platform/coworker/memory/__init__.py | 12 + platform/coworker/memory/base.py | 70 + platform/coworker/memory/sqlite_store.py | 114 + platform/coworker/memory/tools.py | 64 + platform/coworker/overrides.py | 84 + platform/coworker/permissions.py | 168 + platform/coworker/personas/__init__.py | 22 + platform/coworker/personas/builtin/ops.md | 45 + platform/coworker/personas/loading.py | 66 + platform/coworker/personas/manifest.py | 232 + platform/coworker/personas/registry.py | 384 + platform/coworker/project.py | 40 + platform/coworker/providers/__init__.py | 44 + .../coworker/providers/anthropic_provider.py | 380 + platform/coworker/providers/base.py | 90 + platform/coworker/providers/capabilities.py | 51 + .../coworker/providers/gemini_provider.py | 424 + .../coworker/providers/openai_provider.py | 423 + platform/coworker/providers/registry.py | 293 + platform/coworker/providers/router.py | 101 + platform/coworker/risk.py | 58 + platform/coworker/roots.py | 75 + platform/coworker/secrets.py | 178 + platform/coworker/selfwake.py | 162 + platform/coworker/server/__init__.py | 4 + platform/coworker/server/app.py | 913 ++ platform/coworker/server/manager.py | 2269 +++++ platform/coworker/server/run.py | 149 + platform/coworker/sessions.py | 28 + platform/coworker/skills/__init__.py | 3 + platform/coworker/skills/base.py | 115 + platform/coworker/subscriptions.py | 192 + platform/coworker/testing/__init__.py | 1 + .../coworker/testing/fake_slack/__init__.py | 10 + .../coworker/testing/fake_slack/__main__.py | 69 + .../coworker/testing/fake_slack/server.py | 479 ++ platform/coworker/tools/__init__.py | 3 + platform/coworker/tools/ask.py | 55 + platform/coworker/tools/directories.py | 40 + platform/coworker/tools/files.py | 113 + platform/coworker/tools/git.py | 90 + platform/coworker/tools/plan.py | 43 + platform/coworker/tools/registry.py | 71 + platform/coworker/tools/search.py | 179 + platform/coworker/tools/shell.py | 568 ++ platform/coworker/tools/subagent.py | 138 + platform/coworker/tools/todo.py | 81 + platform/coworker/tui/__init__.py | 3 + platform/coworker/tui/app.py | 248 + platform/coworker/unattended.py | 43 + platform/coworker/unrouted.py | 65 + platform/coworker/web/__init__.py | 28 + platform/coworker/web/fetch.py | 117 + platform/coworker/web/providers.py | 128 + platform/coworker/web/tool.py | 94 + platform/docs/config.example.toml | 24 + platform/docs/email-connector-spec.md | 151 + platform/packaging/.gitignore | 4 + platform/packaging/build_dmg.sh | 51 + platform/packaging/build_windows.ps1 | 93 + platform/packaging/coworker-server.spec | 100 + platform/packaging/server_entry.py | 10 + platform/pyproject.toml | 54 + platform/surfaces/gui/.gitignore | 4 + platform/surfaces/gui/README.md | 31 + platform/surfaces/gui/assets/icon.png | Bin 0 -> 21710 bytes platform/surfaces/gui/index.html | 21 + platform/surfaces/gui/package-lock.json | 6013 +++++++++++++ platform/surfaces/gui/package.json | 36 + platform/surfaces/gui/postcss.config.js | 6 + platform/surfaces/gui/src-tauri/.gitignore | 3 + platform/surfaces/gui/src-tauri/Cargo.lock | 5246 ++++++++++++ platform/surfaces/gui/src-tauri/Cargo.toml | 21 + platform/surfaces/gui/src-tauri/build.rs | 3 + .../gui/src-tauri/capabilities/default.json | 15 + .../surfaces/gui/src-tauri/entitlements.plist | 12 + .../surfaces/gui/src-tauri/icons/128x128.png | Bin 0 -> 5619 bytes .../gui/src-tauri/icons/128x128@2x.png | Bin 0 -> 11592 bytes .../surfaces/gui/src-tauri/icons/32x32.png | Bin 0 -> 1306 bytes .../surfaces/gui/src-tauri/icons/64x64.png | Bin 0 -> 2602 bytes .../gui/src-tauri/icons/Square107x107Logo.png | Bin 0 -> 4626 bytes .../gui/src-tauri/icons/Square142x142Logo.png | Bin 0 -> 6227 bytes .../gui/src-tauri/icons/Square150x150Logo.png | Bin 0 -> 6583 bytes .../gui/src-tauri/icons/Square284x284Logo.png | Bin 0 -> 12875 bytes .../gui/src-tauri/icons/Square30x30Logo.png | Bin 0 -> 1193 bytes .../gui/src-tauri/icons/Square310x310Logo.png | Bin 0 -> 13950 bytes .../gui/src-tauri/icons/Square44x44Logo.png | Bin 0 -> 1840 bytes .../gui/src-tauri/icons/Square71x71Logo.png | Bin 0 -> 3036 bytes .../gui/src-tauri/icons/Square89x89Logo.png | Bin 0 -> 3771 bytes .../gui/src-tauri/icons/StoreLogo.png | Bin 0 -> 1992 bytes .../surfaces/gui/src-tauri/icons/icon.icns | Bin 0 -> 111109 bytes .../surfaces/gui/src-tauri/icons/icon.ico | Bin 0 -> 19857 bytes .../surfaces/gui/src-tauri/icons/icon.png | Bin 0 -> 24338 bytes .../surfaces/gui/src-tauri/icons/tray.png | Bin 0 -> 356 bytes .../surfaces/gui/src-tauri/icons/tray.rgba | Bin 0 -> 7744 bytes platform/surfaces/gui/src-tauri/src/lib.rs | 400 + platform/surfaces/gui/src-tauri/src/main.rs | 6 + .../surfaces/gui/src-tauri/tauri.conf.json | 41 + platform/surfaces/gui/src/App.tsx | 1245 +++ platform/surfaces/gui/src/api.ts | 1097 +++ platform/surfaces/gui/src/attach.ts | 25 + .../gui/src/components/AddFolderForm.tsx | 89 + .../gui/src/components/ApprovalCard.tsx | 105 + .../surfaces/gui/src/components/AuditView.tsx | 22 + .../surfaces/gui/src/components/Composer.tsx | 359 + .../components/ConnectorMessageCard.test.tsx | 86 + .../src/components/ConnectorMessageCard.tsx | 98 + .../src/components/DirectoryRequestCard.tsx | 58 + .../surfaces/gui/src/components/Dropdown.tsx | 59 + .../gui/src/components/FolderGate.tsx | 95 + platform/surfaces/gui/src/components/Icon.tsx | 296 + .../gui/src/components/InboxControl.tsx | 86 + .../gui/src/components/InboxItemCard.tsx | 157 + .../surfaces/gui/src/components/InboxView.tsx | 82 + .../gui/src/components/IntegrationsView.tsx | 426 + .../gui/src/components/ManageModal.tsx | 1055 +++ .../surfaces/gui/src/components/Markdown.tsx | 19 + .../gui/src/components/ModelChecklist.tsx | 105 + .../gui/src/components/Onboarding.tsx | 548 ++ .../gui/src/components/PersonaView.test.tsx | 136 + .../gui/src/components/PersonaView.tsx | 265 + .../gui/src/components/PersonasTab.tsx | 154 + .../surfaces/gui/src/components/PlanCard.tsx | 69 + .../surfaces/gui/src/components/RightRail.tsx | 515 ++ .../surfaces/gui/src/components/RootRow.tsx | 52 + .../surfaces/gui/src/components/RootsBar.tsx | 70 + .../gui/src/components/ScheduledView.tsx | 466 ++ .../gui/src/components/SearchModal.tsx | 156 + .../gui/src/components/SessionIntro.tsx | 124 + .../gui/src/components/Sidebar.test.tsx | 134 + .../surfaces/gui/src/components/Sidebar.tsx | 882 ++ .../gui/src/components/SourcesBar.test.tsx | 97 + .../gui/src/components/SourcesBar.tsx | 110 + .../gui/src/components/SourcesDrawer.tsx | 209 + .../gui/src/components/SubscriptionsChip.tsx | 118 + .../surfaces/gui/src/components/TodoPanel.tsx | 19 + .../surfaces/gui/src/components/Toggle.tsx | 31 + .../gui/src/components/Transcript.test.tsx | 35 + .../gui/src/components/Transcript.tsx | 209 + .../gui/src/components/UnattendedToggle.tsx | 75 + .../gui/src/components/personaIcon.tsx | 62 + .../gui/src/connectors/ConnectorIcon.test.tsx | 49 + .../gui/src/connectors/ConnectorIcon.tsx | 102 + .../surfaces/gui/src/connectors/registry.tsx | 161 + .../surfaces/gui/src/connectors/visuals.ts | 43 + .../surfaces/gui/src/itemsFromMessages.ts | 68 + platform/surfaces/gui/src/main.tsx | 14 + platform/surfaces/gui/src/personaScope.ts | 29 + platform/surfaces/gui/src/styles.css | 1253 +++ platform/surfaces/gui/src/tailwind.css | 5 + platform/surfaces/gui/src/tauri.ts | 47 + platform/surfaces/gui/src/theme.ts | 55 + platform/surfaces/gui/src/types.ts | 102 + platform/surfaces/gui/src/useRoots.ts | 75 + platform/surfaces/gui/src/vite-env.d.ts | 1 + platform/surfaces/gui/tailwind.config.js | 48 + platform/surfaces/gui/tsconfig.json | 21 + platform/surfaces/gui/tsconfig.node.json | 11 + platform/surfaces/gui/vite.config.ts | 16 + platform/surfaces/gui/vitest.config.ts | 12 + platform/tests/conftest.py | 25 + platform/tests/test_anthropic_provider.py | 483 ++ platform/tests/test_attachments.py | 179 + platform/tests/test_automation.py | 363 + platform/tests/test_automation_create.py | 73 + platform/tests/test_builtin_personas.py | 53 + platform/tests/test_catalog.py | 115 + platform/tests/test_code_tools.py | 156 + platform/tests/test_config.py | 51 + platform/tests/test_connections.py | 246 + platform/tests/test_connector_registry.py | 107 + platform/tests/test_connectors.py | 1036 +++ platform/tests/test_connectors_allowlist.py | 80 + platform/tests/test_dm_routing.py | 98 + platform/tests/test_durable_resume.py | 93 + platform/tests/test_email_tools.py | 370 + platform/tests/test_engine.py | 319 + platform/tests/test_environment.py | 76 + platform/tests/test_fake_slack.py | 264 + platform/tests/test_gateway_inbox_reply.py | 65 + platform/tests/test_gemini_provider.py | 481 ++ platform/tests/test_inbox.py | 124 + platform/tests/test_inbox_routing.py | 79 + platform/tests/test_interactions.py | 83 + platform/tests/test_mcp.py | 165 + platform/tests/test_memory.py | 189 + platform/tests/test_message_source.py | 210 + platform/tests/test_multiroot.py | 322 + platform/tests/test_permissions_risk.py | 100 + platform/tests/test_persona_connections.py | 219 + platform/tests/test_persona_loading.py | 103 + platform/tests/test_persona_manifest.py | 151 + platform/tests/test_persona_registry.py | 96 + platform/tests/test_plan_mode.py | 250 + platform/tests/test_provider_router.py | 480 ++ platform/tests/test_provider_verify.py | 104 + platform/tests/test_providers.py | 161 + platform/tests/test_risk_overrides.py | 68 + platform/tests/test_secrets.py | 87 + platform/tests/test_self_wake.py | 66 + platform/tests/test_server.py | 449 + platform/tests/test_session_events.py | 124 + platform/tests/test_session_persona.py | 61 + platform/tests/test_settings.py | 159 + platform/tests/test_shell.py | 183 + platform/tests/test_skills.py | 103 + platform/tests/test_subagent.py | 130 + platform/tests/test_subscriptions.py | 167 + platform/tests/test_tools_permissions.py | 151 + platform/tests/test_tui.py | 77 + platform/tests/test_ui_refresh_e2e.py | 306 + platform/tests/test_unattended.py | 48 + platform/tests/test_wake_resume.py | 62 + platform/tests/test_web_search.py | 154 + platform/ui-mocks/redesign.html | 919 ++ poetry.lock | 7428 +++++++++++++++++ pyproject.toml | 93 + scripts/aisuite-code | 5 + tests/__init__.py | 0 tests/agents/__init__.py | 1 + tests/agents/helpers.py | 15 + tests/agents/test_agent.py | 38 + tests/agents/test_agent_integration_flows.py | 106 + tests/agents/test_artifact_store.py | 143 + tests/agents/test_async_runner.py | 85 + tests/agents/test_continuation.py | 227 + tests/agents/test_openai_integration.py | 110 + tests/agents/test_postgres_state_store.py | 182 + tests/agents/test_runner.py | 126 + tests/agents/test_state_store.py | 101 + tests/agents/test_subagent_tool.py | 87 + tests/agents/test_tool_metadata_policy.py | 162 + tests/agents/test_tool_policy.py | 245 + tests/agents/test_trace_output.py | 65 + tests/agents/test_trace_sinks.py | 250 + tests/agents/test_viewer.py | 835 ++ tests/cli/test_aisuite_code_cli.py | 696 ++ tests/client/__init__.py | 0 tests/client/test_async_client.py | 135 + tests/client/test_client.py | 661 ++ tests/client/test_manual_tool_calling.py | 197 + tests/client/test_prerelease.py | 243 + tests/examples/test_dev_cli.py | 285 + tests/framework/test_asr_models.py | 139 + tests/framework/test_asr_params.py | 471 ++ tests/mcp/README.md | 214 + tests/mcp/__init__.py | 1 + tests/mcp/conftest.py | 77 + tests/mcp/test_client.py | 344 + tests/mcp/test_e2e.py | 385 + tests/mcp/test_http_llm_e2e.py | 613 ++ tests/mcp/test_http_transport.py | 797 ++ tests/mcp/test_llm_e2e.py | 381 + tests/providers/__init__.py | 0 tests/providers/test_anthropic_converter.py | 215 + .../test_asr_parameter_passthrough.py | 223 + tests/providers/test_async_provider.py | 53 + tests/providers/test_aws_converter.py | 112 + tests/providers/test_azure_provider.py | 92 + tests/providers/test_cerebras_provider.py | 85 + tests/providers/test_cohere_provider.py | 46 + tests/providers/test_deepgram_provider.py | 241 + tests/providers/test_deepseek_provider.py | 103 + tests/providers/test_google_converter.py | 115 + tests/providers/test_google_provider.py | 345 + tests/providers/test_groq_provider.py | 87 + tests/providers/test_huggingface_provider.py | 242 + tests/providers/test_inception_provider.py | 46 + tests/providers/test_lmstudio_provider.py | 49 + tests/providers/test_mistral_provider.py | 78 + tests/providers/test_nebius_provider.py | 112 + tests/providers/test_ollama_provider.py | 108 + tests/providers/test_openai_provider.py | 256 + tests/providers/test_openrouter_provider.py | 104 + tests/providers/test_sambanova_provider.py | 87 + tests/providers/test_watsonx_provider.py | 64 + tests/test-data/test_audio.mp3 | Bin 0 -> 17153 bytes tests/test_provider.py | 126 + tests/toolkits/test_files.py | 489 ++ tests/toolkits/test_git.py | 33 + tests/toolkits/test_shell.py | 190 + tests/utils/test_async_tools.py | 83 + tests/utils/test_mcp_memory_integration.py | 152 + tests/utils/test_tool_manager.py | 273 + tests/utils/test_tools_mcp_schema.py | 361 + viewer-ui/index.html | 12 + viewer-ui/package-lock.json | 2732 ++++++ viewer-ui/package.json | 24 + viewer-ui/postcss.config.js | 6 + viewer-ui/src/App.jsx | 1206 +++ viewer-ui/src/index.css | 496 ++ viewer-ui/src/main.jsx | 10 + viewer-ui/tailwind.config.js | 18 + viewer-ui/vite.config.js | 10 + 560 files changed, 113225 insertions(+) create mode 100644 .env.sample create mode 100644 .git-blame-ignore-revs create mode 100644 .github/workflows/black.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/run_pytest.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README-alternative.md create mode 100644 README.md create mode 100644 README.wehub.md create mode 100644 aisuite-js/README.md create mode 100644 aisuite-js/examples/.env.example create mode 100644 aisuite-js/examples/basic-usage.ts create mode 100644 aisuite-js/examples/chat-app/.eslintrc.cjs create mode 100644 aisuite-js/examples/chat-app/.gitignore create mode 100644 aisuite-js/examples/chat-app/README.md create mode 100644 aisuite-js/examples/chat-app/index.html create mode 100644 aisuite-js/examples/chat-app/package.json create mode 100644 aisuite-js/examples/chat-app/postcss.config.js create mode 100644 aisuite-js/examples/chat-app/src/App.tsx create mode 100644 aisuite-js/examples/chat-app/src/components/ApiKeyModal.tsx create mode 100644 aisuite-js/examples/chat-app/src/components/ChatContainer.tsx create mode 100644 aisuite-js/examples/chat-app/src/components/ChatInput.tsx create mode 100644 aisuite-js/examples/chat-app/src/components/ChatMessage.tsx create mode 100644 aisuite-js/examples/chat-app/src/components/ModelSelector.tsx create mode 100644 aisuite-js/examples/chat-app/src/components/ProviderSelector.tsx create mode 100644 aisuite-js/examples/chat-app/src/config/llm-config.ts create mode 100644 aisuite-js/examples/chat-app/src/index.css create mode 100644 aisuite-js/examples/chat-app/src/main.tsx create mode 100644 aisuite-js/examples/chat-app/src/services/aisuite-service.ts create mode 100644 aisuite-js/examples/chat-app/src/types/chat.ts create mode 100644 aisuite-js/examples/chat-app/src/utils/cn.ts create mode 100644 aisuite-js/examples/chat-app/tailwind.config.js create mode 100644 aisuite-js/examples/chat-app/tsconfig.json create mode 100644 aisuite-js/examples/chat-app/tsconfig.node.json create mode 100644 aisuite-js/examples/chat-app/vite.config.ts create mode 100644 aisuite-js/examples/deepgram.ts create mode 100644 aisuite-js/examples/groq.ts create mode 100644 aisuite-js/examples/mistral.ts create mode 100644 aisuite-js/examples/openai-asr.ts create mode 100644 aisuite-js/examples/streaming.ts create mode 100644 aisuite-js/examples/test-suite.ts create mode 100644 aisuite-js/examples/tool-calling.ts create mode 100644 aisuite-js/jest.config.ts create mode 100644 aisuite-js/package.json create mode 100644 aisuite-js/src/asr-providers/deepgram/adapters.ts create mode 100644 aisuite-js/src/asr-providers/deepgram/index.ts create mode 100644 aisuite-js/src/asr-providers/deepgram/provider.ts create mode 100644 aisuite-js/src/asr-providers/deepgram/types.ts create mode 100644 aisuite-js/src/asr-providers/index.ts create mode 100644 aisuite-js/src/client.ts create mode 100644 aisuite-js/src/core/base-asr-provider.ts create mode 100644 aisuite-js/src/core/base-provider.ts create mode 100644 aisuite-js/src/core/errors.ts create mode 100644 aisuite-js/src/core/model-parser.ts create mode 100644 aisuite-js/src/index.ts create mode 100644 aisuite-js/src/providers/anthropic/adapters.ts create mode 100644 aisuite-js/src/providers/anthropic/index.ts create mode 100644 aisuite-js/src/providers/anthropic/provider.ts create mode 100644 aisuite-js/src/providers/anthropic/types.ts create mode 100644 aisuite-js/src/providers/groq/adapters.ts create mode 100644 aisuite-js/src/providers/groq/index.ts create mode 100644 aisuite-js/src/providers/groq/provider.ts create mode 100644 aisuite-js/src/providers/groq/types.ts create mode 100644 aisuite-js/src/providers/index.ts create mode 100644 aisuite-js/src/providers/mistral/adapters.ts create mode 100644 aisuite-js/src/providers/mistral/index.ts create mode 100644 aisuite-js/src/providers/mistral/provider.ts create mode 100644 aisuite-js/src/providers/mistral/types.ts create mode 100644 aisuite-js/src/providers/openai/adapters.ts create mode 100644 aisuite-js/src/providers/openai/index.ts create mode 100644 aisuite-js/src/providers/openai/provider.ts create mode 100644 aisuite-js/src/providers/openai/types.ts create mode 100644 aisuite-js/src/types/chat.ts create mode 100644 aisuite-js/src/types/common.ts create mode 100644 aisuite-js/src/types/index.ts create mode 100644 aisuite-js/src/types/providers.ts create mode 100644 aisuite-js/src/types/tools.ts create mode 100644 aisuite-js/src/types/transcription.ts create mode 100644 aisuite-js/src/utils/streaming.ts create mode 100644 aisuite-js/tests/client.test.ts create mode 100644 aisuite-js/tests/providers/anthropic-provider.test.ts create mode 100644 aisuite-js/tests/providers/deepgram-provider.test.ts create mode 100644 aisuite-js/tests/providers/groq-provider.test.ts create mode 100644 aisuite-js/tests/providers/mistral-provider.test.ts create mode 100644 aisuite-js/tests/providers/openai-provider.test.ts create mode 100644 aisuite-js/tests/providers/openai_asr_provider.test.ts create mode 100644 aisuite-js/tests/utils/streaming.test.ts create mode 100644 aisuite-js/tsconfig.json create mode 100644 aisuite/__init__.py create mode 100644 aisuite/agents/__init__.py create mode 100644 aisuite/agents/artifact_store.py create mode 100644 aisuite/agents/artifacts.py create mode 100644 aisuite/agents/context.py create mode 100644 aisuite/agents/policies.py create mode 100644 aisuite/agents/postgres_state_store.py create mode 100644 aisuite/agents/runner.py create mode 100644 aisuite/agents/state_store.py create mode 100644 aisuite/agents/tools.py create mode 100644 aisuite/agents/types.py create mode 100644 aisuite/agents/utils.py create mode 100644 aisuite/agents/viewer.py create mode 100644 aisuite/client.py create mode 100644 aisuite/design-notes/asr-parameter-design-motivation.md create mode 100644 aisuite/framework/__init__.py create mode 100644 aisuite/framework/asr_params.py create mode 100644 aisuite/framework/chat_completion_response.py create mode 100644 aisuite/framework/choice.py create mode 100644 aisuite/framework/message.py create mode 100644 aisuite/framework/parameter_mapper.py create mode 100644 aisuite/framework/provider_interface.py create mode 100644 aisuite/mcp/__init__.py create mode 100644 aisuite/mcp/client.py create mode 100644 aisuite/mcp/config.py create mode 100644 aisuite/mcp/schema_converter.py create mode 100644 aisuite/mcp/tool_wrapper.py create mode 100644 aisuite/provider.py create mode 100644 aisuite/providers/__init__.py create mode 100644 aisuite/providers/anthropic_provider.py create mode 100644 aisuite/providers/aws_provider.py create mode 100644 aisuite/providers/azure_provider.py create mode 100644 aisuite/providers/cerebras_provider.py create mode 100644 aisuite/providers/cohere_provider.py create mode 100644 aisuite/providers/deepgram_provider.py create mode 100644 aisuite/providers/deepseek_provider.py create mode 100644 aisuite/providers/fireworks_provider.py create mode 100644 aisuite/providers/google_provider.py create mode 100644 aisuite/providers/groq_provider.py create mode 100644 aisuite/providers/huggingface_provider.py create mode 100644 aisuite/providers/inception_provider.py create mode 100644 aisuite/providers/lmstudio_provider.py create mode 100644 aisuite/providers/message_converter.py create mode 100644 aisuite/providers/mistral_provider.py create mode 100644 aisuite/providers/nebius_provider.py create mode 100644 aisuite/providers/ollama_provider.py create mode 100644 aisuite/providers/openai_provider.py create mode 100644 aisuite/providers/openrouter_provider.py create mode 100644 aisuite/providers/sambanova_provider.py create mode 100644 aisuite/providers/together_provider.py create mode 100644 aisuite/providers/watsonx_provider.py create mode 100644 aisuite/providers/xai_provider.py create mode 100644 aisuite/toolkits/__init__.py create mode 100644 aisuite/toolkits/files.py create mode 100644 aisuite/toolkits/git.py create mode 100644 aisuite/toolkits/shell.py create mode 100644 aisuite/tracing/__init__.py create mode 100644 aisuite/tracing/normalize.py create mode 100644 aisuite/tracing/sinks.py create mode 100644 aisuite/tracing/static/viewer/assets/index-C29aAR1d.js create mode 100644 aisuite/tracing/static/viewer/assets/index-C2bMCWqj.css create mode 100644 aisuite/tracing/static/viewer/index.html create mode 100644 aisuite/tracing/store.py create mode 100644 aisuite/tracing/viewer.py create mode 100644 aisuite/utils/tools.py create mode 100644 aisuite/utils/utils.py create mode 100644 cli/py/aisuite-code-cli/README.md create mode 100644 cli/py/aisuite-code-cli/TRY_IT.md create mode 100644 cli/py/aisuite-code-cli/aisuite_code_cli/__init__.py create mode 100644 cli/py/aisuite-code-cli/aisuite_code_cli/agent.py create mode 100644 cli/py/aisuite-code-cli/aisuite_code_cli/app.py create mode 100644 cli/py/aisuite-code-cli/aisuite_code_cli/approval.py create mode 100644 cli/py/aisuite-code-cli/aisuite_code_cli/config.py create mode 100644 cli/py/aisuite-code-cli/aisuite_code_cli/main.py create mode 100644 cli/py/aisuite-code-cli/aisuite_code_cli/rendering.py create mode 100644 cli/py/aisuite-code-cli/poetry.lock create mode 100644 cli/py/aisuite-code-cli/pyproject.toml create mode 100644 docs/agents-quickstart.md create mode 100644 docs/chat-completions-quickstart.md create mode 100644 docs/opencoworker-quickstart.md create mode 100644 examples/AISuiteDemo.ipynb create mode 100644 examples/DeepseekPost.ipynb create mode 100644 examples/QnA_with_pdf.ipynb create mode 100644 examples/agents/agent_api_quickstart.ipynb create mode 100644 examples/agents/local_observability_demo.ipynb create mode 100644 examples/agents/movie_buff_assistant.ipynb create mode 100644 examples/agents/recipe_chef_assistant.ipynb create mode 100644 examples/agents/simple_agent.py create mode 100644 examples/agents/snake_game_generator.ipynb create mode 100644 examples/agents/stock_dashboard.html create mode 100644 examples/agents/stock_market_dashboard.html create mode 100644 examples/agents/stock_market_mini_tracker.ipynb create mode 100644 examples/agents/stock_market_tracker.ipynb create mode 100644 examples/agents/world_weather_dashboard.ipynb create mode 100644 examples/aisuite_tool_abstraction.ipynb create mode 100644 examples/asr_example.ipynb create mode 100644 examples/chat-ui/.streamlit/config.toml create mode 100644 examples/chat-ui/README.md create mode 100644 examples/chat-ui/chat.py create mode 100644 examples/chat-ui/config.yaml create mode 100644 examples/cli/create_demo_trace.py create mode 100644 examples/cli/dev.py create mode 100644 examples/client.ipynb create mode 100644 examples/llm_reasoning.ipynb create mode 100644 examples/mcp_config_dict_example.py create mode 100644 examples/mcp_http_example.py create mode 100644 examples/mcp_tools_example.ipynb create mode 100644 examples/simple_tool_calling.ipynb create mode 100644 examples/tool_calling_abstraction.ipynb create mode 100644 guides/README.md create mode 100644 guides/anthropic.md create mode 100644 guides/aws.md create mode 100644 guides/azure.md create mode 100644 guides/cerebras.md create mode 100644 guides/cohere.md create mode 100644 guides/deepseek.md create mode 100644 guides/google.md create mode 100644 guides/groq.md create mode 100644 guides/huggingface.md create mode 100644 guides/lmstudio.md create mode 100644 guides/mistral.md create mode 100644 guides/nebius.md create mode 100644 guides/ollama.md create mode 100644 guides/openai.md create mode 100644 guides/sambanova.md create mode 100644 guides/watsonx.md create mode 100644 guides/xai.md create mode 100644 platform/.gitignore create mode 100644 platform/coworker/__init__.py create mode 100644 platform/coworker/agent.py create mode 100644 platform/coworker/agents/__init__.py create mode 100644 platform/coworker/agents/base.py create mode 100644 platform/coworker/agents/chat.py create mode 100644 platform/coworker/agents/code.py create mode 100644 platform/coworker/agents/cowork.py create mode 100644 platform/coworker/agents/myhelper.py create mode 100644 platform/coworker/agents/registry.py create mode 100644 platform/coworker/attachments.py create mode 100644 platform/coworker/audit.py create mode 100644 platform/coworker/automation/__init__.py create mode 100644 platform/coworker/automation/models.py create mode 100644 platform/coworker/automation/scheduler.py create mode 100644 platform/coworker/automation/store.py create mode 100644 platform/coworker/automation/tools.py create mode 100644 platform/coworker/catalog.py create mode 100644 platform/coworker/cli.py create mode 100644 platform/coworker/config.py create mode 100644 platform/coworker/connections.py create mode 100644 platform/coworker/connectors/__init__.py create mode 100644 platform/coworker/connectors/adapters.py create mode 100644 platform/coworker/connectors/base.py create mode 100644 platform/coworker/connectors/browser_automation.py create mode 100644 platform/coworker/connectors/cli.py create mode 100644 platform/coworker/connectors/config.py create mode 100644 platform/coworker/connectors/descriptors.py create mode 100644 platform/coworker/connectors/email_tools.py create mode 100644 platform/coworker/connectors/experimental/__init__.py create mode 100644 platform/coworker/connectors/fake.py create mode 100644 platform/coworker/connectors/gateway.py create mode 100644 platform/coworker/connectors/integration_tools.py create mode 100644 platform/coworker/connectors/senders.py create mode 100644 platform/coworker/connectors/setup.py create mode 100644 platform/coworker/connectors/tool_defs.py create mode 100644 platform/coworker/connectors/tools.py create mode 100644 platform/coworker/conversations.py create mode 100644 platform/coworker/engine.py create mode 100644 platform/coworker/environment.py create mode 100644 platform/coworker/events.py create mode 100644 platform/coworker/inbox.py create mode 100644 platform/coworker/inbox_routing.py create mode 100644 platform/coworker/interactions.py create mode 100644 platform/coworker/mcp/__init__.py create mode 100644 platform/coworker/mcp/client.py create mode 100644 platform/coworker/mcp/config.py create mode 100644 platform/coworker/mcp/tools.py create mode 100644 platform/coworker/memory/__init__.py create mode 100644 platform/coworker/memory/base.py create mode 100644 platform/coworker/memory/sqlite_store.py create mode 100644 platform/coworker/memory/tools.py create mode 100644 platform/coworker/overrides.py create mode 100644 platform/coworker/permissions.py create mode 100644 platform/coworker/personas/__init__.py create mode 100644 platform/coworker/personas/builtin/ops.md create mode 100644 platform/coworker/personas/loading.py create mode 100644 platform/coworker/personas/manifest.py create mode 100644 platform/coworker/personas/registry.py create mode 100644 platform/coworker/project.py create mode 100644 platform/coworker/providers/__init__.py create mode 100644 platform/coworker/providers/anthropic_provider.py create mode 100644 platform/coworker/providers/base.py create mode 100644 platform/coworker/providers/capabilities.py create mode 100644 platform/coworker/providers/gemini_provider.py create mode 100644 platform/coworker/providers/openai_provider.py create mode 100644 platform/coworker/providers/registry.py create mode 100644 platform/coworker/providers/router.py create mode 100644 platform/coworker/risk.py create mode 100644 platform/coworker/roots.py create mode 100644 platform/coworker/secrets.py create mode 100644 platform/coworker/selfwake.py create mode 100644 platform/coworker/server/__init__.py create mode 100644 platform/coworker/server/app.py create mode 100644 platform/coworker/server/manager.py create mode 100644 platform/coworker/server/run.py create mode 100644 platform/coworker/sessions.py create mode 100644 platform/coworker/skills/__init__.py create mode 100644 platform/coworker/skills/base.py create mode 100644 platform/coworker/subscriptions.py create mode 100644 platform/coworker/testing/__init__.py create mode 100644 platform/coworker/testing/fake_slack/__init__.py create mode 100644 platform/coworker/testing/fake_slack/__main__.py create mode 100644 platform/coworker/testing/fake_slack/server.py create mode 100644 platform/coworker/tools/__init__.py create mode 100644 platform/coworker/tools/ask.py create mode 100644 platform/coworker/tools/directories.py create mode 100644 platform/coworker/tools/files.py create mode 100644 platform/coworker/tools/git.py create mode 100644 platform/coworker/tools/plan.py create mode 100644 platform/coworker/tools/registry.py create mode 100644 platform/coworker/tools/search.py create mode 100644 platform/coworker/tools/shell.py create mode 100644 platform/coworker/tools/subagent.py create mode 100644 platform/coworker/tools/todo.py create mode 100644 platform/coworker/tui/__init__.py create mode 100644 platform/coworker/tui/app.py create mode 100644 platform/coworker/unattended.py create mode 100644 platform/coworker/unrouted.py create mode 100644 platform/coworker/web/__init__.py create mode 100644 platform/coworker/web/fetch.py create mode 100644 platform/coworker/web/providers.py create mode 100644 platform/coworker/web/tool.py create mode 100644 platform/docs/config.example.toml create mode 100644 platform/docs/email-connector-spec.md create mode 100644 platform/packaging/.gitignore create mode 100755 platform/packaging/build_dmg.sh create mode 100644 platform/packaging/build_windows.ps1 create mode 100644 platform/packaging/coworker-server.spec create mode 100644 platform/packaging/server_entry.py create mode 100644 platform/pyproject.toml create mode 100644 platform/surfaces/gui/.gitignore create mode 100644 platform/surfaces/gui/README.md create mode 100644 platform/surfaces/gui/assets/icon.png create mode 100644 platform/surfaces/gui/index.html create mode 100644 platform/surfaces/gui/package-lock.json create mode 100644 platform/surfaces/gui/package.json create mode 100644 platform/surfaces/gui/postcss.config.js create mode 100644 platform/surfaces/gui/src-tauri/.gitignore create mode 100644 platform/surfaces/gui/src-tauri/Cargo.lock create mode 100644 platform/surfaces/gui/src-tauri/Cargo.toml create mode 100644 platform/surfaces/gui/src-tauri/build.rs create mode 100644 platform/surfaces/gui/src-tauri/capabilities/default.json create mode 100644 platform/surfaces/gui/src-tauri/entitlements.plist create mode 100644 platform/surfaces/gui/src-tauri/icons/128x128.png create mode 100644 platform/surfaces/gui/src-tauri/icons/128x128@2x.png create mode 100644 platform/surfaces/gui/src-tauri/icons/32x32.png create mode 100644 platform/surfaces/gui/src-tauri/icons/64x64.png create mode 100644 platform/surfaces/gui/src-tauri/icons/Square107x107Logo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/Square142x142Logo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/Square150x150Logo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/Square284x284Logo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/Square30x30Logo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/Square310x310Logo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/Square44x44Logo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/Square71x71Logo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/Square89x89Logo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/StoreLogo.png create mode 100644 platform/surfaces/gui/src-tauri/icons/icon.icns create mode 100644 platform/surfaces/gui/src-tauri/icons/icon.ico create mode 100644 platform/surfaces/gui/src-tauri/icons/icon.png create mode 100644 platform/surfaces/gui/src-tauri/icons/tray.png create mode 100644 platform/surfaces/gui/src-tauri/icons/tray.rgba create mode 100644 platform/surfaces/gui/src-tauri/src/lib.rs create mode 100644 platform/surfaces/gui/src-tauri/src/main.rs create mode 100644 platform/surfaces/gui/src-tauri/tauri.conf.json create mode 100644 platform/surfaces/gui/src/App.tsx create mode 100644 platform/surfaces/gui/src/api.ts create mode 100644 platform/surfaces/gui/src/attach.ts create mode 100644 platform/surfaces/gui/src/components/AddFolderForm.tsx create mode 100644 platform/surfaces/gui/src/components/ApprovalCard.tsx create mode 100644 platform/surfaces/gui/src/components/AuditView.tsx create mode 100644 platform/surfaces/gui/src/components/Composer.tsx create mode 100644 platform/surfaces/gui/src/components/ConnectorMessageCard.test.tsx create mode 100644 platform/surfaces/gui/src/components/ConnectorMessageCard.tsx create mode 100644 platform/surfaces/gui/src/components/DirectoryRequestCard.tsx create mode 100644 platform/surfaces/gui/src/components/Dropdown.tsx create mode 100644 platform/surfaces/gui/src/components/FolderGate.tsx create mode 100644 platform/surfaces/gui/src/components/Icon.tsx create mode 100644 platform/surfaces/gui/src/components/InboxControl.tsx create mode 100644 platform/surfaces/gui/src/components/InboxItemCard.tsx create mode 100644 platform/surfaces/gui/src/components/InboxView.tsx create mode 100644 platform/surfaces/gui/src/components/IntegrationsView.tsx create mode 100644 platform/surfaces/gui/src/components/ManageModal.tsx create mode 100644 platform/surfaces/gui/src/components/Markdown.tsx create mode 100644 platform/surfaces/gui/src/components/ModelChecklist.tsx create mode 100644 platform/surfaces/gui/src/components/Onboarding.tsx create mode 100644 platform/surfaces/gui/src/components/PersonaView.test.tsx create mode 100644 platform/surfaces/gui/src/components/PersonaView.tsx create mode 100644 platform/surfaces/gui/src/components/PersonasTab.tsx create mode 100644 platform/surfaces/gui/src/components/PlanCard.tsx create mode 100644 platform/surfaces/gui/src/components/RightRail.tsx create mode 100644 platform/surfaces/gui/src/components/RootRow.tsx create mode 100644 platform/surfaces/gui/src/components/RootsBar.tsx create mode 100644 platform/surfaces/gui/src/components/ScheduledView.tsx create mode 100644 platform/surfaces/gui/src/components/SearchModal.tsx create mode 100644 platform/surfaces/gui/src/components/SessionIntro.tsx create mode 100644 platform/surfaces/gui/src/components/Sidebar.test.tsx create mode 100644 platform/surfaces/gui/src/components/Sidebar.tsx create mode 100644 platform/surfaces/gui/src/components/SourcesBar.test.tsx create mode 100644 platform/surfaces/gui/src/components/SourcesBar.tsx create mode 100644 platform/surfaces/gui/src/components/SourcesDrawer.tsx create mode 100644 platform/surfaces/gui/src/components/SubscriptionsChip.tsx create mode 100644 platform/surfaces/gui/src/components/TodoPanel.tsx create mode 100644 platform/surfaces/gui/src/components/Toggle.tsx create mode 100644 platform/surfaces/gui/src/components/Transcript.test.tsx create mode 100644 platform/surfaces/gui/src/components/Transcript.tsx create mode 100644 platform/surfaces/gui/src/components/UnattendedToggle.tsx create mode 100644 platform/surfaces/gui/src/components/personaIcon.tsx create mode 100644 platform/surfaces/gui/src/connectors/ConnectorIcon.test.tsx create mode 100644 platform/surfaces/gui/src/connectors/ConnectorIcon.tsx create mode 100644 platform/surfaces/gui/src/connectors/registry.tsx create mode 100644 platform/surfaces/gui/src/connectors/visuals.ts create mode 100644 platform/surfaces/gui/src/itemsFromMessages.ts create mode 100644 platform/surfaces/gui/src/main.tsx create mode 100644 platform/surfaces/gui/src/personaScope.ts create mode 100644 platform/surfaces/gui/src/styles.css create mode 100644 platform/surfaces/gui/src/tailwind.css create mode 100644 platform/surfaces/gui/src/tauri.ts create mode 100644 platform/surfaces/gui/src/theme.ts create mode 100644 platform/surfaces/gui/src/types.ts create mode 100644 platform/surfaces/gui/src/useRoots.ts create mode 100644 platform/surfaces/gui/src/vite-env.d.ts create mode 100644 platform/surfaces/gui/tailwind.config.js create mode 100644 platform/surfaces/gui/tsconfig.json create mode 100644 platform/surfaces/gui/tsconfig.node.json create mode 100644 platform/surfaces/gui/vite.config.ts create mode 100644 platform/surfaces/gui/vitest.config.ts create mode 100644 platform/tests/conftest.py create mode 100644 platform/tests/test_anthropic_provider.py create mode 100644 platform/tests/test_attachments.py create mode 100644 platform/tests/test_automation.py create mode 100644 platform/tests/test_automation_create.py create mode 100644 platform/tests/test_builtin_personas.py create mode 100644 platform/tests/test_catalog.py create mode 100644 platform/tests/test_code_tools.py create mode 100644 platform/tests/test_config.py create mode 100644 platform/tests/test_connections.py create mode 100644 platform/tests/test_connector_registry.py create mode 100644 platform/tests/test_connectors.py create mode 100644 platform/tests/test_connectors_allowlist.py create mode 100644 platform/tests/test_dm_routing.py create mode 100644 platform/tests/test_durable_resume.py create mode 100644 platform/tests/test_email_tools.py create mode 100644 platform/tests/test_engine.py create mode 100644 platform/tests/test_environment.py create mode 100644 platform/tests/test_fake_slack.py create mode 100644 platform/tests/test_gateway_inbox_reply.py create mode 100644 platform/tests/test_gemini_provider.py create mode 100644 platform/tests/test_inbox.py create mode 100644 platform/tests/test_inbox_routing.py create mode 100644 platform/tests/test_interactions.py create mode 100644 platform/tests/test_mcp.py create mode 100644 platform/tests/test_memory.py create mode 100644 platform/tests/test_message_source.py create mode 100644 platform/tests/test_multiroot.py create mode 100644 platform/tests/test_permissions_risk.py create mode 100644 platform/tests/test_persona_connections.py create mode 100644 platform/tests/test_persona_loading.py create mode 100644 platform/tests/test_persona_manifest.py create mode 100644 platform/tests/test_persona_registry.py create mode 100644 platform/tests/test_plan_mode.py create mode 100644 platform/tests/test_provider_router.py create mode 100644 platform/tests/test_provider_verify.py create mode 100644 platform/tests/test_providers.py create mode 100644 platform/tests/test_risk_overrides.py create mode 100644 platform/tests/test_secrets.py create mode 100644 platform/tests/test_self_wake.py create mode 100644 platform/tests/test_server.py create mode 100644 platform/tests/test_session_events.py create mode 100644 platform/tests/test_session_persona.py create mode 100644 platform/tests/test_settings.py create mode 100644 platform/tests/test_shell.py create mode 100644 platform/tests/test_skills.py create mode 100644 platform/tests/test_subagent.py create mode 100644 platform/tests/test_subscriptions.py create mode 100644 platform/tests/test_tools_permissions.py create mode 100644 platform/tests/test_tui.py create mode 100644 platform/tests/test_ui_refresh_e2e.py create mode 100644 platform/tests/test_unattended.py create mode 100644 platform/tests/test_wake_resume.py create mode 100644 platform/tests/test_web_search.py create mode 100644 platform/ui-mocks/redesign.html create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100755 scripts/aisuite-code create mode 100644 tests/__init__.py create mode 100644 tests/agents/__init__.py create mode 100644 tests/agents/helpers.py create mode 100644 tests/agents/test_agent.py create mode 100644 tests/agents/test_agent_integration_flows.py create mode 100644 tests/agents/test_artifact_store.py create mode 100644 tests/agents/test_async_runner.py create mode 100644 tests/agents/test_continuation.py create mode 100644 tests/agents/test_openai_integration.py create mode 100644 tests/agents/test_postgres_state_store.py create mode 100644 tests/agents/test_runner.py create mode 100644 tests/agents/test_state_store.py create mode 100644 tests/agents/test_subagent_tool.py create mode 100644 tests/agents/test_tool_metadata_policy.py create mode 100644 tests/agents/test_tool_policy.py create mode 100644 tests/agents/test_trace_output.py create mode 100644 tests/agents/test_trace_sinks.py create mode 100644 tests/agents/test_viewer.py create mode 100644 tests/cli/test_aisuite_code_cli.py create mode 100644 tests/client/__init__.py create mode 100644 tests/client/test_async_client.py create mode 100644 tests/client/test_client.py create mode 100644 tests/client/test_manual_tool_calling.py create mode 100644 tests/client/test_prerelease.py create mode 100644 tests/examples/test_dev_cli.py create mode 100644 tests/framework/test_asr_models.py create mode 100644 tests/framework/test_asr_params.py create mode 100644 tests/mcp/README.md create mode 100644 tests/mcp/__init__.py create mode 100644 tests/mcp/conftest.py create mode 100644 tests/mcp/test_client.py create mode 100644 tests/mcp/test_e2e.py create mode 100644 tests/mcp/test_http_llm_e2e.py create mode 100644 tests/mcp/test_http_transport.py create mode 100644 tests/mcp/test_llm_e2e.py create mode 100644 tests/providers/__init__.py create mode 100644 tests/providers/test_anthropic_converter.py create mode 100644 tests/providers/test_asr_parameter_passthrough.py create mode 100644 tests/providers/test_async_provider.py create mode 100644 tests/providers/test_aws_converter.py create mode 100644 tests/providers/test_azure_provider.py create mode 100644 tests/providers/test_cerebras_provider.py create mode 100644 tests/providers/test_cohere_provider.py create mode 100644 tests/providers/test_deepgram_provider.py create mode 100644 tests/providers/test_deepseek_provider.py create mode 100644 tests/providers/test_google_converter.py create mode 100644 tests/providers/test_google_provider.py create mode 100644 tests/providers/test_groq_provider.py create mode 100644 tests/providers/test_huggingface_provider.py create mode 100644 tests/providers/test_inception_provider.py create mode 100644 tests/providers/test_lmstudio_provider.py create mode 100644 tests/providers/test_mistral_provider.py create mode 100644 tests/providers/test_nebius_provider.py create mode 100644 tests/providers/test_ollama_provider.py create mode 100644 tests/providers/test_openai_provider.py create mode 100644 tests/providers/test_openrouter_provider.py create mode 100644 tests/providers/test_sambanova_provider.py create mode 100644 tests/providers/test_watsonx_provider.py create mode 100644 tests/test-data/test_audio.mp3 create mode 100644 tests/test_provider.py create mode 100644 tests/toolkits/test_files.py create mode 100644 tests/toolkits/test_git.py create mode 100644 tests/toolkits/test_shell.py create mode 100644 tests/utils/test_async_tools.py create mode 100644 tests/utils/test_mcp_memory_integration.py create mode 100644 tests/utils/test_tool_manager.py create mode 100644 tests/utils/test_tools_mcp_schema.py create mode 100644 viewer-ui/index.html create mode 100644 viewer-ui/package-lock.json create mode 100644 viewer-ui/package.json create mode 100644 viewer-ui/postcss.config.js create mode 100644 viewer-ui/src/App.jsx create mode 100644 viewer-ui/src/index.css create mode 100644 viewer-ui/src/main.jsx create mode 100644 viewer-ui/tailwind.config.js create mode 100644 viewer-ui/vite.config.js diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..405e8d0 --- /dev/null +++ b/.env.sample @@ -0,0 +1,50 @@ +# OpenAI API Key +OPENAI_API_KEY= + +# Anthropic API Key +ANTHROPIC_API_KEY= + +# AWS SDK credentials +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_REGION= + +# Azure +AZURE_API_KEY= + +# Cerebras +CEREBRAS_API_KEY= + +# Google Cloud +GOOGLE_APPLICATION_CREDENTIALS=./google-adc +GOOGLE_REGION= +GOOGLE_PROJECT_ID= + +# Hugging Face token +HF_TOKEN= + +# Fireworks +FIREWORKS_API_KEY= + +# Mistral +MISTRAL_API_KEY= + +# Together AI +TOGETHER_API_KEY= + +# WatsonX +WATSONX_SERVICE_URL= +WATSONX_API_KEY= +WATSONX_PROJECT_ID= + +# xAI +XAI_API_KEY= + +# Sambanova +SAMBANOVA_API_KEY= + +# Inception Labs +INCEPTION_API_KEY= + +# OpenRouter Labs +OPENROUTER_API_KEY= diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..bfdacd7 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Mechanical, repo-wide black formatting (style: black-format the entire repo) +41b1591f6d121cee2462e7ac204fc4ac60075d61 diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 0000000..0a8b8e9 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,10 @@ +name: Lint + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: psf/black@stable \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f239c7e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,154 @@ +# Desktop release builds — macOS (.dmg, arm64 + Intel) and Windows (.msi + NSIS .exe). +# +# CI calls the SAME scripts developers run locally (platform/packaging/build_dmg.sh and +# build_windows.ps1); this file only provisions the toolchain (Node, Rust, a Python venv at +# platform/.venv with PyInstaller) and publishes the results. +# +# Triggers: +# - tag push `v*` → builds all targets and attaches them to a DRAFT GitHub Release +# (review, then publish by hand). +# - manual run → builds and uploads workflow artifacts only (no release). +# +# Each installer is uploaded twice: once with its versioned name (archive) and once with a +# stable name (OpenCoworker-macos-arm64.dmg, …) so the website can link to +# github.com//releases/latest/download/ +# and never need updating. +# +# macOS signing + notarization: Tauri's bundler handles both during `tauri build` when the +# APPLE_* env vars are present (import cert → sign app + sidecar with hardened runtime → +# notarize via notarytool → staple). Driven by repo secrets: +# APPLE_CERTIFICATE base64 .p12 (Developer ID Application cert + key) +# APPLE_CERTIFICATE_PASSWORD the .p12 export password +# APPLE_SIGNING_IDENTITY e.g. "Developer ID Application: Name (TEAMID)" +# APPLE_API_KEY_CONTENT base64 App Store Connect API .p8 (notarytool) +# APPLE_API_KEY the API key id +# APPLE_API_ISSUER the API issuer id +# When the secrets are absent (forks, scratch runs) the build degrades to unsigned — +# installable via `xattr -cr`. Windows remains unsigned (Authenticode is a later step). + +name: Release + +on: + push: + tags: ["v*", "app-v*"] + workflow_dispatch: + +defaults: + run: + shell: bash + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + # No Intel macOS target: macos-13 (the last Intel runner image) is deprecated and + # its queue waits run to hours, which blocks the release job. Intel Macs are + # 2020-and-earlier hardware — revisit only if beta users actually ask. + - os: macos-latest # Apple Silicon + slug: macos-arm64 + - os: windows-latest + slug: windows + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: platform/surfaces/gui/package-lock.json + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: platform/surfaces/gui/src-tauri + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Set up the sidecar venv (platform/.venv) + # The build scripts expect a venv at platform/.venv with the package + PyInstaller. + # typer/tzdata are build-time-only (PyInstaller walks mcp.cli, which needs typer; + # tzdata ships zoneinfo for Windows). The .pth puts the repo-root `aisuite` package + # on the venv's path (it isn't pip-installed — same as local dev). + run: | + python -m venv platform/.venv + if [ "$RUNNER_OS" = "Windows" ]; then VPY=platform/.venv/Scripts/python; else VPY=platform/.venv/bin/python; fi + "$VPY" -m pip install --upgrade pip + "$VPY" -m pip install -e platform pyinstaller typer tzdata + "$VPY" -c "import sysconfig, pathlib; pathlib.Path(sysconfig.get_paths()['purelib'], 'aisuite-repo.pth').write_text(str(pathlib.Path('.').resolve()))" + "$VPY" -c "import aisuite, coworker" # fail fast if either import breaks + + - name: npm ci + working-directory: platform/surfaces/gui + run: npm ci + + - name: Build .dmg (macOS) + if: runner.os == 'macOS' + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + APPLE_API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY_CONTENT }} + APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} + APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} + run: | + # Unset empty APPLE_* vars so runs without secrets stay cleanly unsigned + # (Tauri treats a present-but-empty var as a config error). + for v in APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_SIGNING_IDENTITY APPLE_API_KEY APPLE_API_ISSUER; do + [ -n "$(eval echo "\${$v:-}")" ] || unset "$v" + done + if [ -n "${APPLE_API_KEY_CONTENT:-}" ]; then + echo "$APPLE_API_KEY_CONTENT" | base64 -d > "$RUNNER_TEMP/AuthKey.p8" + export APPLE_API_KEY_PATH="$RUNNER_TEMP/AuthKey.p8" + fi + unset APPLE_API_KEY_CONTENT + bash platform/packaging/build_dmg.sh + + - name: Build .msi + NSIS .exe (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: ./platform/packaging/build_windows.ps1 + + - name: Stage artifacts (versioned + stable names) + run: | + mkdir -p out + BUNDLE=platform/surfaces/gui/src-tauri/target/release/bundle + if [ "$RUNNER_OS" = "Windows" ]; then + cp "$BUNDLE"/nsis/*.exe out/ + cp "$BUNDLE"/nsis/*.exe out/OpenCoworker-windows-setup.exe + cp "$BUNDLE"/msi/*.msi out/ + cp "$BUNDLE"/msi/*.msi out/OpenCoworker-windows.msi + else + cp "$BUNDLE"/dmg/*.dmg out/ + cp "$BUNDLE"/dmg/*.dmg out/OpenCoworker-${{ matrix.slug }}.dmg + fi + ls -la out + + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.slug }} + path: out/* + if-no-files-found: error + + release: + if: startsWith(github.ref, 'refs/tags/v') + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + + - uses: softprops/action-gh-release@v2 + with: + draft: true + files: dist/* + generate_release_notes: true diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml new file mode 100644 index 0000000..d873172 --- /dev/null +++ b/.github/workflows/run_pytest.yml @@ -0,0 +1,24 @@ +name: Lint + +on: [push, pull_request] + +jobs: + build_and_test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ "3.10", "3.11", "3.12" ] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + poetry install --all-extras --with test + - name: Test with pytest + run: poetry run pytest -m "not integration" + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7783a8e --- /dev/null +++ b/.gitignore @@ -0,0 +1,71 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +.env +.venv +env/ +venv/ +ENV/ +*.whl + +# Node/TypeScript +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.npm +.env.local +.env.*.local +dist/ +coverage/ +*.tsbuildinfo + +# IDEs and editors +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store +**/.DS_Store +*.sublime-workspace +*.sublime-project + +# Jupyter Notebook +.ipynb_checkpoints +*/.ipynb_checkpoints/* + +# Testing +.coverage +htmlcov/ +.pytest_cache/ +coverage/ +.nyc_output/ + +# Cloud credentials +.google-adc + +# Logs +logs +*.log + +# Python version +.python-version diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ec9e84e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,11 @@ +repos: + # Using this mirror lets us use mypyc-compiled black, which is about 2x faster + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 24.4.2 + hooks: + - id: black + # It is recommended to specify the latest version of Python + # supported by your project here, or alternatively use + # pre-commit's default_language_version, see + # https://pre-commit.com/#top_level-default_language_version + language_version: python3.12 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b7eb908 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,213 @@ + +# Contributing to aisuite + +First off, thanks for taking the time to contribute! + +All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) +for different ways to help and details about how this project handles them. Please make sure to read +the relevant section before making your contribution. It will make it a lot easier for us maintainers +and smooth out the experience for all involved. The community looks forward to your contributions. + +> And if you like the project, but just don't have time to contribute, that's fine. There are other easy +> ways to support the project and show your appreciation, which we would also be very happy about: +> - Star the project +> - Tweet about it +> - Refer this project in your project's readme +> - Mention the project at local meetups and tell your friends/colleagues + + +## Table of Contents + +- [I Have a Question](#i-have-a-question) +- [I Want To Contribute](#i-want-to-contribute) + - [Reporting Bugs](#reporting-bugs) + - [Suggesting Enhancements](#suggesting-enhancements) + - [Your First Code Contribution](#your-first-code-contribution) + - [Improving The Documentation](#improving-the-documentation) +- [Styleguides](#styleguides) + - [Commit Messages](#commit-messages) + + + + +## I Have a Question + +> If you want to ask a question, we assume that you have read the available +> [Documentation](https://github.com/andrewyng/aisuite/blob/main/README.md). + +Before you ask a question, it is best to search for existing [Issues](https://github.com/andrewyng/aisuite/issues) +that might help you. If you find a relevant issue that already exists and still need clarification, please add your question to that existing issue. We also recommend reaching out to the community in the aisuite [Discord](https://discord.gg/T6Nvn8ExSb) server. + +If you then still feel the need to ask a question and need clarification, we recommend the following: + +- Open an [Issue](https://github.com/andrewyng/aisuite/issues/new). +- Provide as much context as you can about what you're running into. +- Provide project and platform versions (python, OS, etc.), depending on what seems relevant. + +We (or someone in the community) will then take care of the issue as soon as possible. + + +## I Want To Contribute + +> ### Legal Notice +> When contributing to this project, you must agree that you have authored 100% of the content, that +> you have the necessary rights to the content and that the content you contribute may be provided +> under the project license. + +### Reporting Bugs + + +#### Before Submitting a Bug Report + +A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask +you to investigate carefully, collect information and describe the issue in detail in your report. Please +complete the following steps in advance to help us fix any potential bug as fast as possible. + +- Make sure that you are using the latest version. +- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment + components/versions (Make sure that you have read the [documentation](https://github.com/andrewyng/aisuite/blob/main/README.md). + If you are looking for support, you might want to check [this section](#i-have-a-question)). +- To see if other users have experienced (and potentially already solved) the same issue you are having, + check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/andrewyng/aisuite?q=label%3Abug). +- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub + community have discussed the issue. +- Collect information about the bug: + - Stack trace (Traceback) + - OS, Platform and Version (Windows, Linux, macOS, x86, ARM) + - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on + what seems relevant. + - Possibly your input and the output + - Can you reliably reproduce the issue? And can you also reproduce it with older versions? + + +#### How Do I Submit a Good Bug Report? + +> You must never report security related issues, vulnerabilities or bugs including sensitive information to +> the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to . + + +We use GitHub issues to track bugs and errors. If you run into an issue with the project: + +- Open an [Issue](https://github.com/andrewyng/aisuite/issues/new). (Since we can't be sure at + this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) +- Explain the behavior you would expect and the actual behavior. +- Please provide as much context as possible and describe the *reproduction steps* that someone else can + follow to recreate the issue on their own. This usually includes your code. For good bug reports you + should isolate the problem and create a reduced test case. +- Provide the information you collected in the previous section. + +Once it's filed: + +- The project team will label the issue accordingly. +- A team member will try to reproduce the issue with your provided steps. If there are no reproduction + steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the + issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced. +- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other + tags (such as `critical`), and the issue will be left to be + [implemented by someone](#your-first-code-contribution). + +Please use the issue templates provided. + + +### Suggesting Enhancements + +This section guides you through submitting an enhancement suggestion for aisuite, +**including completely new features and minor improvements to existing functionality**. Following these +guidelines will help maintainers and the community to understand your suggestion and find related suggestions. + + +#### Before Submitting an Enhancement + +- Make sure that you are using the latest version. +- Read the [documentation](https://github.com/andrewyng/aisuite/blob/main/README.md) carefully + and find out if the functionality is already covered, maybe by an individual configuration. +- Perform a [search](https://github.com/andrewyng/aisuite/issues) to see if the enhancement has + already been suggested. If it has, add a comment to the existing issue instead of opening a new one. +- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong + case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. + + +#### How Do I Submit a Good Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://github.com/andrewyng/aisuite/issues). + +- Use a **clear and descriptive title** for the issue to identify the suggestion. +- Provide a **step-by-step description of the suggested enhancement** in as many details as possible. +- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. + At this point you can also tell which alternatives do not work for you. +- **Explain why this enhancement would be useful** to most aisuite users. You may also want to + point out the other projects that solved it better and which could serve as inspiration. + + +### Your First Code Contribution + +#### Pre-requisites + +You should first [fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) +the `aisuite` repository and then clone your forked repository: + +```bash +git clone https://github.com//aisuite.git +``` + + + +Once in the cloned repository directory, make a branch on the forked repository with your username and +description of PR: +```bash +git checkout -B / +``` + +Please install the development and test dependencies: +```bash +poetry install --with dev,test +``` + +`aisuite` uses pre-commit to ensure the formatting is consistent: +```bash +pre-commit install +``` + +**Make suggested changes** + +Afterwards, our suite of formatting tests will run automatically before each `git commit`. You can also +run these manually: +```bash +pre-commit run --all-files +``` + +If a formatting test fails, it will fix the modified code in place and abort the `git commit`. After looking +over the changes, you can `git add ` and then repeat the previous git commit command. + +**Note**: a github workflow will check the files with the same formatter and reject the PR if it doesn't +pass, so please make sure it passes locally. + + +#### Testing +`aisuite` tracks unit tests. Pytest is used to execute said unit tests in `tests/`: + +```bash +poetry run pytest tests +``` + +If your code changes implement a new function, please make a corresponding unit test to the `test/*` files. + +#### Contributing Workflow +We actively welcome your pull requests. + +1. Create your new branch from main in your forked repo, with your username and a name describing the work + you're completing e.g. user-123/add-feature-x. +2. If you've added code that should be tested, add tests. Ensure all tests pass. See the testing section + for more information. +3. If you've changed APIs, update the documentation. +4. Make sure your code lints. + + + +### Improving The Documentation +We welcome valuable contributions in the form of new documentation or revised documentation that provide +further clarity or accuracy. Each function should be clearly documented. Well-documented code is easier +to review and understand/extend. + +## Styleguides +For code documentation, please follow the [Google styleguide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..60d3824 --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2024 Andrew Ng + +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. diff --git a/README-alternative.md b/README-alternative.md new file mode 100644 index 0000000..8ff2f30 --- /dev/null +++ b/README-alternative.md @@ -0,0 +1,132 @@ +# aisuite + +> **🆕 Introducing Open Coworker** — an open agent harness that runs tasks and +> automations on your machine. Desktop app, your own API keys, your files. +> **→ [Get started in 2 minutes](docs/coworker/quickstart.md)** + +[![PyPI](https://img.shields.io/pypi/v/aisuite.svg)](https://pypi.org/project/aisuite/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Build](https://github.com/andrewyng/aisuite/actions/workflows/run_pytest.yml/badge.svg)](https://github.com/andrewyng/aisuite/actions) + +Simple, unified tools for building with LLMs — from a drop-in chat client to a +full agent harness. Three layers, each usable on its own. + +--- + +## Chat Completions + +A uniform client across 20+ providers using an OpenAI-compatible interface. Swap +models with a string — no SDK juggling, no rewrites. + +```python +import aisuite as ai + +client = ai.Client() +response = client.chat.completions.create( + model="anthropic:claude-sonnet-4-5", + messages=[{"role": "user", "content": "Why is the sky blue?"}], +) +print(response.choices[0].message.content) +``` + +```bash +pip install aisuite # add provider extras, e.g. aisuite[anthropic] +``` + +→ [examples](examples/chat-completion) · [docs](docs/chat) + +--- + +## Agent API + +A lightweight `Agent` + `Runner` built on the client — tools, continuation +state, and tracing, without adopting a heavy framework. The agent is the +reusable definition; the runner owns execution. + +```python +import aisuite as ai + +def get_weather(city: str) -> str: + """Get the current weather for a city.""" + return f"It's sunny in {city}." + +agent = ai.Agent( + name="assistant", + model="openai:gpt-5.5", + instructions="Answer briefly. Use tools when they help.", + tools=[get_weather], +) + +result = ai.Runner.run_sync(agent, "What's the weather in San Francisco?") +print(result.final_output) +``` + +```bash +pip install aisuite[agents] +``` + +→ [examples](examples/agents-api) · [docs](docs/agents) + +--- + +## Open Coworker + +An open agent harness for automating daily tasks and more — a desktop app where +an agent works in your folders, uses connectors and tools, produces artifacts, +and runs scheduled automations. Bring your own API keys; your files and keys stay +on your machine. + +- **Multi-folder file access** with per-folder read-only / read-write grants +- **Connectors & tools** — browser automation, integrations, MCP servers +- **Artifacts** — Markdown, images, PDF, CSV, spreadsheets, and Office files +- **Automations** — scheduled runs that continue as conversations +- **Your models** — OpenAI, Anthropic, Gemini, and local models via Ollama + +**[Download for macOS / Windows](https://github.com/andrewyng/aisuite/releases)** +· [quickstart](docs/coworker/quickstart.md) · [docs](docs/coworker) + +--- + +## How they stack + +Each layer builds on the one below it, and each is usable on its own: + +``` +Open Coworker an agent harness for tasks & automations (app) + │ built on +Agent API Agent + Runner: tools, state, tracing (lib) + │ built on +Chat Completions one client, many providers (lib) +``` + +Start at whatever layer fits: call a model, wrap it in an agent, or run the whole +harness. + +--- + +## Repository layout + +``` +libs/ + aisuite-py/ the `aisuite` package — Chat Completions + Agent API + aisuite-js/ JavaScript/TypeScript port +apps/ + opencoworker/ the Open Coworker harness (desktop app + server) + code-cli/ aisuite-code, the command-line coding agent +examples/ runnable examples per product +docs/ chat / agents / coworker +``` + +## Documentation + +- **Chat Completions** — [docs/chat](docs/chat) +- **Agent API** — [docs/agents](docs/agents) +- **Open Coworker** — [docs/coworker](docs/coworker) + +## Contributing + +We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) to get started. + +## License + +[MIT](LICENSE) diff --git a/README.md b/README.md new file mode 100644 index 0000000..579fa90 --- /dev/null +++ b/README.md @@ -0,0 +1,219 @@ +> ![NEW](https://img.shields.io/badge/%E2%9C%A8_NEW-8250df?style=for-the-badge) +> ## OpenCoworker +> **An AI agent that lives on your desktop, built on aisuite.** +> +> OpenCoworker is a desktop AI agent that can not only chat, but also do deep research and carry out tasks for +> you on your computer. It can read files (with permission) to gain context, read/send messages (slack, email, etc.), +> and create real deliverables like PDF reports, documents, spreadsheets. It also supports scheduled automations, +> such as providing you a daily news summary. +> +> Requires bringing your own API key (OpenAI, Anthropic, Google) or run fully local with Ollama. Your data stays on your machine. +> +> [**⬇ Download for macOS**](https://github.com/andrewyng/aisuite/releases/latest/download/OpenCoworker-macos-arm64.dmg) +>   macOS 13+ (Apple Silicon)    +> +> [**⬇ Download for Windows**](https://github.com/andrewyng/aisuite/releases/latest/download/OpenCoworker-windows-setup.exe) +>   Windows 10/11 (x64)  ·  +> +> [**Quickstart:**](docs/opencoworker-quickstart.md) — install, connect a model, first tasks, automations. +> +> Its source lives in this repository under `platform/` — a working reference for building your own agent harness on aisuite. + +
+ +# aisuite + +[![PyPI](https://img.shields.io/pypi/v/aisuite)](https://pypi.org/project/aisuite/) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) + +`aisuite` is a lightweight Python library for building with LLMs, in two layers: a unified **Chat Completions API** across providers, and an **Agents API** with tools and toolkits on top. This repo is also home to **OpenCoworker**, a desktop AI coworker built using aisuite: + +```text +┌───────────────────────────────────────────────┐ +│ OpenCoworker │ agent harness for doing everyday tasks +├───────────────────────────────────────────────┤ +│ Agents API · Toolkits · MCP │ build agents across multiple LLMs +├───────────────────────────────────────────────┤ +│ Chat Completions API │ one API across multiple LLM providers +├────────┬───────────┬────────┬────────┬────────┤ +│ OpenAI │ Anthropic │ Google │ Ollama │ Others │ +└────────┴───────────┴────────┴────────┴────────┘ +``` + +* **[Chat Completions API](#chat-completions)** — a unified, OpenAI-style interface for *OpenAI, Anthropic, Google, Mistral, Hugging Face, AWS, Cohere, Ollama, OpenRouter*, and more. Swap providers by changing one string. +* **[Agents API · Toolkits · MCP](#agents)** — give models real Python functions as tools, run multi-turn loops, attach ready-made toolkits (files, git, shell) or any MCP server, and govern it all with tool policies. +* **[OpenCoworker](docs/opencoworker-quickstart.md)** — a desktop AI coworker built using aisuite, shipped as an app for everyday tasks. + +--- + +## Installation + +### The aisuite library (Python) + +Install the base package, or include the SDKs of the providers you plan to use: + +```shell +pip install aisuite # base package, no provider SDKs +pip install 'aisuite[anthropic]' # with a specific provider's SDK +pip install 'aisuite[all]' # with all provider SDKs +``` + +You'll also need API keys for the providers you call — the [Chat Completions quickstart](docs/chat-completions-quickstart.md) covers key setup and your first calls. + +### The OpenCoworker app (desktop) + +Download the installer and bring your own API key (or run local models with Ollama): + +[**⬇ macOS (Apple Silicon)**](https://github.com/andrewyng/aisuite/releases/latest/download/OpenCoworker-macos-arm64.dmg)  ·  [**⬇ Windows 10/11 (x64)**](https://github.com/andrewyng/aisuite/releases/latest/download/OpenCoworker-windows-setup.exe)  ·  [OpenCoworker quickstart](docs/opencoworker-quickstart.md) + +--- + + +## Chat Completions — one API across providers + +The chat API provides a high-level abstraction for model interactions. It supports all core parameters (`temperature`, `max_tokens`, `tools`, etc.) in a provider-agnostic way, and standardizes request and response structures so you can focus on logic rather than SDK differences. + +Model names use the format `:`; aisuite routes the call to the right provider with the right parameters: + +```python +import aisuite as ai +client = ai.Client() + +models = ["openai:gpt-4o", "anthropic:claude-3-5-sonnet-20240620"] + +messages = [ + {"role": "system", "content": "Respond in Pirate English."}, + {"role": "user", "content": "Tell me a joke."}, +] + +for model in models: + response = client.chat.completions.create( + model=model, + messages=messages, + temperature=0.75 + ) + print(response.choices[0].message.content) +``` + +**→ Quickstart:** [docs/chat-completions-quickstart.md](docs/chat-completions-quickstart.md) — install, key setup, local models, and more examples. + +--- + + +## Agents — give models real tools + +aisuite turns tool calling into a one-liner: pass plain Python functions and it generates the schemas, executes the calls, and feeds results back to the model. + +### Tool calling with `max_turns` + +```python +def will_it_rain(location: str, time_of_day: str): + """Check if it will rain in a location at a given time today. + + Args: + location (str): Name of the city + time_of_day (str): Time of the day in HH:MM format. + """ + return "YES" + +client = ai.Client() +response = client.chat.completions.create( + model="openai:gpt-4o", + messages=[{ + "role": "user", + "content": "I live in San Francisco. Can you check for weather " + "and plan an outdoor picnic for me at 2pm?" + }], + tools=[will_it_rain], + max_turns=2 # Maximum number of back-and-forth tool calls +) +print(response.choices[0].message.content) +``` + +With `max_turns` set, aisuite sends your message, executes any tool calls the model requests, returns the results to the model, and repeats until the conversation completes. `response.choices[0].intermediate_messages` carries the full tool interaction history if you want to continue the conversation. + +Prefer full manual control? Omit `max_turns` and pass OpenAI-format JSON tool specs — aisuite returns the model's tool-call requests and you run the loop yourself. See `examples/tool_calling_abstraction.ipynb` for both styles. + +### The Agents API + +For longer-running, structured work there is a first-class Agents API: declare an agent once, run it with a `Runner`, and attach **toolkits** — prebuilt, sandboxed tool families for files, git, and shell: + +```python +import aisuite as ai +from aisuite import Agent, Runner + +agent = Agent( + name="repo-helper", + model="anthropic:claude-sonnet-4-6", + instructions="You are a careful repo assistant. Use your tools to answer from the code.", + tools=[*ai.toolkits.files(root="."), *ai.toolkits.git(root=".")], +) + +result = Runner.run(agent, "What changed in the last commit? Summarize in 3 bullets.") +print(result.final_output) +``` + +The Agents API also gives you the pieces a production harness needs: + +* **Tool policies** — `RequireApprovalPolicy`, allow/deny lists, or your own callable deciding which tool calls run. +* **State stores** — persist and resume runs (in-memory, file, or Postgres) and continue conversations across processes. +* **Artifacts & tracing** — capture what an agent produced and every step it took along the way. + +### MCP tools + +aisuite natively supports the [Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro), so any MCP server's tools can be handed to a model without boilerplate (`pip install 'aisuite[mcp]'`): + +```python +client = ai.Client() +response = client.chat.completions.create( + model="openai:gpt-4o", + messages=[{"role": "user", "content": "List the files in the current directory"}], + tools=[{ + "type": "mcp", + "name": "filesystem", + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/directory"] + }], + max_turns=3 +) +print(response.choices[0].message.content) +``` + +For reusable connections, security filters, and tool prefixing, use the explicit `MCPClient`. + +**→ Quickstart:** [docs/agents-quickstart.md](docs/agents-quickstart.md) — manual tool handling, the full Agents API, policies, state stores, and MCP in depth. + +--- + +## Extending aisuite: Adding a Provider + +New providers can be added by implementing a lightweight adapter. The system uses a naming convention for discovery: + +| Element | Convention | +| --------------- | ---------------------------------- | +| **Module file** | `_provider.py` | +| **Class name** | `Provider` (capitalized) | + +Example: + +```python +# providers/openai_provider.py +class OpenaiProvider(BaseProvider): + ... +``` + +This convention ensures consistency and enables automatic loading of new integrations. + +--- + +## Contributing + +Contributions are welcome. Please review the [Contributing Guide](https://github.com/andrewyng/aisuite/blob/main/CONTRIBUTING.md) and join our [Discord](https://discord.gg/T6Nvn8ExSb) for discussions. + +--- + +## License + +Released under the **MIT License** — free for commercial and non-commercial use. + +--- diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..4631ace --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`andrewyng/aisuite` +- 原始仓库:https://github.com/andrewyng/aisuite +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/aisuite-js/README.md b/aisuite-js/README.md new file mode 100644 index 0000000..d4c630d --- /dev/null +++ b/aisuite-js/README.md @@ -0,0 +1,304 @@ +# AISuite + +AISuite is a unified TypeScript library that provides a single, consistent interface for interacting with multiple Large Language Model (LLM) providers. The library uses OpenAI's API format as the standard interface while supporting OpenAI and Anthropic Claude. + +npm pacakge - `npm i aisuite` + +## Features + +- **Unified API**: Single interface compatible with OpenAI's API structure +- **Multi-Provider Support**: Currently supports OpenAI and Anthropic +- **Provider Selection**: Use `provider:model` format (e.g., `openai:gpt-4o`, `anthropic:claude-3-haiku-20240307`) +- **Tool Calling**: Transparent tool/function calling across all providers +- **Streaming**: Real-time streaming responses with consistent API +- **Type Safety**: Full TypeScript support with comprehensive type definitions +- **Error Handling**: Unified error handling across providers +- **Speech-to-Text**: Automatic Speech Recognition (ASR) support with multiple providers (OpenAI Whisper, Deepgram) + +## Installation + +```bash +npm install aisuite +``` + +## Quick Start + +```typescript +import { Client } from 'aisuite'; + +const client = new Client({ + openai: { + apiKey: process.env.OPENAI_API_KEY, + }, + anthropic: { apiKey: process.env.ANTHROPIC_API_KEY }, + deepgram: { apiKey: process.env.DEEPGRAM_API_KEY }, +}); + +// Use any provider with identical interface +const response = await client.chat.completions.create({ + model: 'openai:gpt-4o', + messages: [ + { role: 'system', content: 'You are a helpful assistant.' }, + { role: 'user', content: 'Hello!' } + ], +}); + +console.log(response.choices[0].message.content); +``` + +## Usage Examples + +### Basic Chat Completion + +```typescript +// OpenAI +const openaiResponse = await client.chat.completions.create({ + model: 'openai:gpt-4o', + messages: [ + { role: 'system', content: 'You are a helpful assistant.' }, + { role: 'user', content: 'What is TypeScript?' } + ], + temperature: 0.7, + max_tokens: 1000, +}); + +// Anthropic - exact same interface +const anthropicResponse = await client.chat.completions.create({ + model: 'anthropic:claude-3-haiku-20240307', + messages: [ + { role: 'system', content: 'You are a helpful assistant.' }, + { role: 'user', content: 'What is TypeScript?' } + ], + temperature: 0.7, + max_tokens: 1000, +}); +``` + +### Tool/Function Calling + +```typescript +const tools = [ + { + type: 'function' as const, + function: { + name: 'get_weather', + description: 'Get current weather for a location', + parameters: { + type: 'object', + properties: { + location: { type: 'string', description: 'City name' } + }, + required: ['location'] + } + } + } +]; + +// Works identically across all providers +const response = await client.chat.completions.create({ + model: 'anthropic:claude-3-haiku-20240307', + messages: [{ role: 'user', content: 'What\'s the weather in NYC?' }], + tools, + tool_choice: 'auto' +}); + +if (response.choices[0].message.tool_calls) { + console.log('Tool calls:', response.choices[0].message.tool_calls); +} +``` + +### Streaming Responses + +```typescript +const stream = await client.chat.completions.create({ + model: 'openai:gpt-4o', + messages: [{ role: 'user', content: 'Tell me a story' }], + stream: true +}); + +// TypeScript: cast to AsyncIterable +for await (const chunk of stream as AsyncIterable) { + process.stdout.write(chunk.choices[0]?.delta?.content || ''); +} +``` + +### Streaming with Abort Controller + +```typescript +const controller = new AbortController(); + +// Abort after 5 seconds +setTimeout(() => controller.abort(), 5000); + +const stream = await client.chat.completions.create({ + model: 'anthropic:claude-3-haiku-20240307', + messages: [{ role: 'user', content: 'Write a long story' }], + stream: true +}, { signal: controller.signal }); + +try { + for await (const chunk of stream as AsyncIterable) { + process.stdout.write(chunk.choices[0]?.delta?.content || ''); + } +} catch (error) { + if (error.name === 'AbortError') { + console.log('Stream aborted'); + } +} +``` + +### Speech-to-Text Transcription + +```typescript +// Initialize client with audio support for OpenAI +const client = new Client({ + openai: { + apiKey: process.env.OPENAI_API_KEY, + }, + deepgram: { apiKey: process.env.DEEPGRAM_API_KEY } +}); + +// Using Deepgram +const deepgramResponse = await client.audio.transcriptions.create({ + model: 'deepgram:nova-2', + file: audioBuffer, // Buffer containing audio data + language: 'en-US', + timestamps: true, + word_confidence: true, + speaker_labels: true, +}); + +// Using OpenAI Whisper +const openaiResponse = await client.audio.transcriptions.create({ + model: 'openai:whisper-1', + file: audioBuffer, + language: 'en', + response_format: 'verbose_json', + temperature: 0, + timestamps: true, +}); + +console.log('Transcribed Text:', openaiResponse.text); +console.log('Words with timestamps:', openaiResponse.words); +``` + +### Error Handling + +```typescript +import { AISuiteError, ProviderNotConfiguredError } from 'aisuite'; + +try { + const response = await client.chat.completions.create({ + model: 'invalid:model', + messages: [{ role: 'user', content: 'Hello' }] + }); +} catch (error) { + if (error instanceof ProviderNotConfiguredError) { + console.error('Provider not configured:', error.message); + } else if (error instanceof AISuiteError) { + console.error('AISuite error:', error.message, error.provider); + } else { + console.error('Unknown error:', error); + } +} +``` + +## API Reference + +### Client Configuration + +```typescript +const client = new Client({ + openai?: { + apiKey: string; + baseURL?: string; + organization?: string; + }, + anthropic?: { + apiKey: string; + baseURL?: string; + }, + deepgram?: { + apiKey: string; + baseURL?: string; + } +}); +``` + +### Chat Completion Request + +All providers use the standard OpenAI chat completion format: + +```typescript +interface ChatCompletionRequest { + model: string; // "provider:model" format + messages: ChatMessage[]; + tools?: Tool[]; + tool_choice?: ToolChoice; + temperature?: number; + max_tokens?: number; + stop?: string | string[]; + stream?: boolean; +} +``` + +### Transcription Request + +All ASR providers use a standard transcription request format with additional provider-specific parameters: + +```typescript +interface TranscriptionRequest { + model: string; // "provider:model" format + file: Buffer; // Audio file as Buffer + language?: string; // Language code (e.g., "en", "en-US") + timestamps?: boolean; // Include word-level timestamps + [key: string]: any; // Additional provider-specific parameters: + // For OpenAI: See https://platform.openai.com/docs/api-reference/audio/createTranscription + // For Deepgram: See https://developers.deepgram.com/reference/speech-to-text-api/listen +} +``` + +### Helper Methods + +```typescript +// List all configured providers (including ASR) +client.listProviders(); // ['openai', 'anthropic'] +client.listASRProviders(); // ['deepgram', 'openai'] + +// Check if a provider is configured +client.isProviderConfigured('openai'); // true +client.isASRProviderConfigured('deepgram'); // true +``` + +## Current Limitations + +- Only OpenAI and Anthropic providers are currently supported for chat (Gemini, Mistral, and Bedrock coming soon) +- Tool calling requires handling tool responses manually +- Streaming tool calls require manual accumulation of arguments +- ASR support is limited to OpenAI Whisper (requires explicit audio configuration) and Deepgram +- Some provider-specific ASR features might require using provider-specific parameters + +## Development + +```bash +# Install dependencies +npm install + +# Build the project +npm run build + +# Run tests +npm test + +# Run examples +#Run basic usage example only: +npm run example:basic +# Run tool calling example only: +npm run example:tools +# Run the full test suite: +npm run test:examples +``` + +## License + +MIT diff --git a/aisuite-js/examples/.env.example b/aisuite-js/examples/.env.example new file mode 100644 index 0000000..9467aac --- /dev/null +++ b/aisuite-js/examples/.env.example @@ -0,0 +1,5 @@ +# Copy this file to .env and fill in your API keys +OPENAI_API_KEY=your-openai-api-key-here +ANTHROPIC_API_KEY=your-anthropic-api-key-here +GROQ_API_KEY=your-groq-api-key-here +MISTRAL_API_KEY=your-mistral-api-key-here \ No newline at end of file diff --git a/aisuite-js/examples/basic-usage.ts b/aisuite-js/examples/basic-usage.ts new file mode 100644 index 0000000..2e35afe --- /dev/null +++ b/aisuite-js/examples/basic-usage.ts @@ -0,0 +1,66 @@ +import 'dotenv/config'; +import { Client } from '../src'; + +async function main() { + // Initialize the client with API keys + const client = new Client({ + openai: { apiKey: process.env.OPENAI_API_KEY! }, + anthropic: { apiKey: process.env.ANTHROPIC_API_KEY! }, + }); + + console.log('Available providers:', client.listProviders()); + + // Example 1: OpenAI Chat Completion + console.log('\n--- OpenAI Example ---'); + try { + const openaiResponse = await client.chat.completions.create({ + model: 'openai:gpt-4o-mini', + messages: [ + { role: 'system', content: 'You are a helpful assistant.' }, + { role: 'user', content: 'What is TypeScript in one sentence?' } + ], + temperature: 0.7, + max_tokens: 100, + }); + + console.log('OpenAI Response:', openaiResponse.choices[0].message.content); + console.log('Usage:', openaiResponse.usage); + console.log('Full response:', JSON.stringify(openaiResponse, null, 2)); + } catch (error) { + console.error('OpenAI Error:', error); + } + + // Example 2: Anthropic Chat Completion + console.log('\n--- Anthropic Example ---'); + try { + const anthropicResponse = await client.chat.completions.create({ + model: 'anthropic:claude-3-haiku-20240307', + messages: [ + { role: 'system', content: 'You are a helpful assistant.' }, + { role: 'user', content: 'What is TypeScript in one sentence?' } + ], + temperature: 0.7, + max_tokens: 100, + }); + + console.log('Anthropic Response:', anthropicResponse.choices[0].message.content); + console.log('Usage:', anthropicResponse.usage); + console.log('Full response:', JSON.stringify(anthropicResponse, null, 2)); + } catch (error) { + console.error('Anthropic Error:', error); + } + + // Example 3: Error handling - invalid provider + console.log('\n--- Error Handling Example ---'); + try { + await client.chat.completions.create({ + model: 'invalid:model', + messages: [{ role: 'user', content: 'Hello' }] + }); + } catch (error) { + console.error('Expected error:', error); + } +} + +// Run the examples +main().catch(console.error); \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/.eslintrc.cjs b/aisuite-js/examples/chat-app/.eslintrc.cjs new file mode 100644 index 0000000..5b7620c --- /dev/null +++ b/aisuite-js/examples/chat-app/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + '@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/.gitignore b/aisuite-js/examples/chat-app/.gitignore new file mode 100644 index 0000000..db93c6e --- /dev/null +++ b/aisuite-js/examples/chat-app/.gitignore @@ -0,0 +1,31 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/README.md b/aisuite-js/examples/chat-app/README.md new file mode 100644 index 0000000..b3bed59 --- /dev/null +++ b/aisuite-js/examples/chat-app/README.md @@ -0,0 +1,210 @@ +# AISuite Chat App + +A modern React TypeScript chat application built with AISuite, allowing you to chat with multiple AI models and compare their responses in real-time. + +## Features + +- **Multi-Provider Support**: Chat with OpenAI, Anthropic, Groq, and Mistral models +- **Comparison Mode**: Compare responses from two different AI models side-by-side +- **Modern UI**: Clean, responsive interface built with React and Tailwind CSS +- **Real-time Chat**: Instant messaging with AI models +- **API Key Management**: Secure storage and management of API keys +- **Error Handling**: Comprehensive error handling and user feedback +- **TypeScript**: Full type safety throughout the application + +## Prerequisites + +- Node.js 18+ +- npm or yarn +- API keys for the AI providers you want to use: + - OpenAI API key + - Anthropic API key + - Groq API key + - Mistral API key + +## Installation + +1. Clone the repository and navigate to the chat app directory: +```bash +cd aisuite-js/chat-app +``` + +2. Install dependencies: +```bash +npm install +``` + +3. Start the development server: +```bash +npm run dev +``` + +4. Open your browser and navigate to `http://localhost:3000` + +## Configuration + +### API Keys + +1. Click the "Configure API Keys" button in the header +2. Enter your API keys for the providers you want to use +3. Click "Save" to store the configuration + +The app will automatically save your API keys to localStorage for future use. + +### Supported Models + +The app comes pre-configured with the following models: + +**OpenAI:** +- GPT-4o +- GPT-4o Mini + +**Anthropic:** +- Claude 3.5 Sonnet +- Claude 3 Haiku + +**Groq:** +- Llama 3.1 8B +- Mixtral 8x7B + +**Mistral:** +- Mistral 7B +- Mistral Large + +## Usage + +### Basic Chat + +1. Configure your API keys +2. Select a model from the dropdown +3. Type your message and press Enter or click Send +4. View the AI response + +### Comparison Mode + +1. Enable "Comparison Mode" checkbox +2. Select two different models +3. Send a message to see responses from both models side-by-side +4. Compare the different responses and capabilities + +### Chat Management + +- **Reset Chat**: Click the reset button to clear all chat history +- **Model Switching**: Change models at any time during the conversation +- **Error Handling**: The app displays clear error messages for API issues + +## Sample Queries + +Try these sample queries to test the different models: + +``` +"What is the weather in Tokyo?" +``` + +``` +"Write a poem about the weather in Tokyo." +``` + +``` +"Write a python program to print the fibonacci sequence." +``` + +``` +"Write test cases for this program." +``` + +## Development + +### Project Structure + +``` +src/ +├── components/ # React components +│ ├── ApiKeyModal.tsx +│ ├── ChatContainer.tsx +│ ├── ChatInput.tsx +│ ├── ChatMessage.tsx +│ └── ModelSelector.tsx +├── config/ # Configuration files +│ └── llm-config.ts +├── services/ # Business logic +│ └── aisuite-service.ts +├── types/ # TypeScript type definitions +│ └── chat.ts +├── App.tsx # Main application component +├── main.tsx # Application entry point +└── index.css # Global styles +``` + +### Available Scripts + +- `npm run dev` - Start development server +- `npm run build` - Build for production +- `npm run preview` - Preview production build +- `npm run lint` - Run ESLint + +### Adding New Models + +To add new models, edit `src/config/llm-config.ts`: + +```typescript +export const configuredLLMs: LLMConfig[] = [ + // ... existing models + { + name: "Your New Model", + provider: "provider-name", + model: "model-name" + } +]; +``` + +### Styling + +The app uses Tailwind CSS for styling. The design system includes: + +- Light and dark mode support +- Responsive design +- Custom scrollbars +- Loading animations +- Error states + +## Technologies Used + +- **React 18** - UI framework +- **TypeScript** - Type safety +- **Vite** - Build tool and dev server +- **Tailwind CSS** - Styling +- **Lucide React** - Icons +- **AISuite** - AI provider abstraction + +## Browser Support + +- Chrome 90+ +- Firefox 88+ +- Safari 14+ +- Edge 90+ + +## Contributing + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Add tests if applicable +5. Submit a pull request + +## License + +MIT License - see the main repository for details. + +## Support + +For issues and questions: +- Check the [AISuite documentation](https://github.com/andrewyng/aisuite) +- Open an issue in the repository +- Check the console for error messages + +## Security Notes + +- API keys are stored in localStorage (client-side only) +- No API keys are sent to any server except the AI providers +- Consider using environment variables for production deployments \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/index.html b/aisuite-js/examples/chat-app/index.html new file mode 100644 index 0000000..1d87ca4 --- /dev/null +++ b/aisuite-js/examples/chat-app/index.html @@ -0,0 +1,13 @@ + + + + + + + AISuite Chat App + + +
+ + + \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/package.json b/aisuite-js/examples/chat-app/package.json new file mode 100644 index 0000000..2865d71 --- /dev/null +++ b/aisuite-js/examples/chat-app/package.json @@ -0,0 +1,34 @@ +{ + "name": "aisuite-chat-app", + "version": "1.0.0", + "description": "A React TypeScript chat application using AISuite", + "private": true, + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "lucide-react": "^0.263.1", + "clsx": "^2.0.0", + "tailwind-merge": "^1.14.0" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "autoprefixer": "^10.4.14", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "postcss": "^8.4.27", + "tailwindcss": "^3.3.3", + "typescript": "^5.0.2", + "vite": "^4.4.5" + } +} \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/postcss.config.js b/aisuite-js/examples/chat-app/postcss.config.js new file mode 100644 index 0000000..387612e --- /dev/null +++ b/aisuite-js/examples/chat-app/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/src/App.tsx b/aisuite-js/examples/chat-app/src/App.tsx new file mode 100644 index 0000000..200a4f8 --- /dev/null +++ b/aisuite-js/examples/chat-app/src/App.tsx @@ -0,0 +1,333 @@ +import React, { useState, useEffect } from 'react'; +import { Settings, AlertCircle } from 'lucide-react'; +import { Message, AISuiteConfig } from './types/chat'; +import { configuredLLMs, getLLMConfigByName } from './config/llm-config'; +import { aiSuiteService } from './services/aisuite-service'; +import { ChatContainer } from './components/ChatContainer'; +import { ChatInput } from './components/ChatInput'; +import { ModelSelector } from './components/ModelSelector'; +import { ProviderSelector } from './components/ProviderSelector'; +import { ApiKeyModal } from './components/ApiKeyModal'; + +function App() { + const [chatHistory1, setChatHistory1] = useState([]); + const [chatHistory2, setChatHistory2] = useState([]); + const [isProcessing, setIsProcessing] = useState(false); + const [useComparisonMode, setUseComparisonMode] = useState(false); + const [selectedProvider, setSelectedProvider] = useState(''); + const [selectedModel1, setSelectedModel1] = useState(''); + const [selectedModel2, setSelectedModel2] = useState(''); + const [showApiKeyModal, setShowApiKeyModal] = useState(false); + const [apiConfig, setApiConfig] = useState({}); + const [error, setError] = useState(null); + + // Initialize AISuite service when API config changes + useEffect(() => { + if (Object.keys(apiConfig).length > 0) { + try { + aiSuiteService.initialize(apiConfig); + setError(null); + } catch (err) { + setError('Failed to initialize AISuite client'); + } + } + }, [apiConfig]); + + // Load API config from localStorage on mount + useEffect(() => { + const savedConfig = localStorage.getItem('aisuite-config'); + if (savedConfig) { + try { + const config = JSON.parse(savedConfig); + setApiConfig(config); + } catch (err) { + console.error('Failed to load saved config'); + } + } + }, []); + + const handleSendMessage = async (message: string) => { + if (!message.trim()) return; + + // Check if provider is selected + if (!selectedProvider) { + setError('Please select a provider first'); + return; + } + + // Check if API key is configured for the selected provider + if (!apiConfig[selectedProvider as keyof AISuiteConfig]?.apiKey) { + setError(`API key for ${selectedProvider} is not configured. Please configure it first.`); + setShowApiKeyModal(true); + return; + } + + const userMessage: Message = { + role: 'user', + content: message, + timestamp: new Date() + }; + + setIsProcessing(true); + setError(null); + + try { + // Add user message to both chat histories + setChatHistory1(prev => [...prev, userMessage]); + if (useComparisonMode) { + setChatHistory2(prev => [...prev, userMessage]); + } + + // Get model configurations + const modelConfig1 = getLLMConfigByName(selectedModel1); + if (!modelConfig1) { + throw new Error(`Model ${selectedModel1} not found`); + } + + // Query first model + const response1 = await aiSuiteService.queryLLM(modelConfig1, [...chatHistory1, userMessage]); + const assistantMessage1: Message = { + role: 'assistant', + content: response1, + timestamp: new Date() + }; + setChatHistory1(prev => [...prev, assistantMessage1]); + + // Query second model if in comparison mode + if (useComparisonMode) { + const modelConfig2 = getLLMConfigByName(selectedModel2); + if (!modelConfig2) { + throw new Error(`Model ${selectedModel2} not found`); + } + + const response2 = await aiSuiteService.queryLLM(modelConfig2, [...chatHistory2, userMessage]); + const assistantMessage2: Message = { + role: 'assistant', + content: response2, + timestamp: new Date() + }; + setChatHistory2(prev => [...prev, assistantMessage2]); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'An error occurred'); + } finally { + setIsProcessing(false); + } + }; + + const handleResetChat = () => { + setChatHistory1([]); + setChatHistory2([]); + setError(null); + }; + + const handleSaveApiConfig = (config: AISuiteConfig) => { + setApiConfig(config); + localStorage.setItem('aisuite-config', JSON.stringify(config)); + }; + + // Get all available providers (show all by default) + const allProviders = ['openai', 'anthropic', 'groq', 'mistral']; + const availableProviders = allProviders; + + // Get configured providers (those with API keys) + const configuredProviders = Object.keys(apiConfig).filter(provider => + apiConfig[provider as keyof AISuiteConfig]?.apiKey + ); + + // Get models for the selected provider + const availableModels = selectedProvider + ? configuredLLMs.filter(model => model.provider === selectedProvider) + : []; + + // Reset model selections when provider changes + useEffect(() => { + if (selectedProvider) { + const providerModels = configuredLLMs.filter(model => model.provider === selectedProvider); + if (providerModels.length > 0) { + setSelectedModel1(providerModels[0].name); + if (useComparisonMode && providerModels.length > 1) { + setSelectedModel2(providerModels[1].name); + } else { + setSelectedModel2(''); + } + } else { + setSelectedModel1(''); + setSelectedModel2(''); + } + } else { + setSelectedModel1(''); + setSelectedModel2(''); + } + }, [selectedProvider, useComparisonMode]); + + const hasConfiguredProviders = Object.keys(apiConfig).length > 0; + + return ( +
+ {/* Header */} +
+
+
+

AISuite Chat

+ +
+
+
+ + {/* Main Content */} +
+ {!hasConfiguredProviders ? ( +
+
+ +

No API Keys Configured

+

+ Please configure your API keys to start chatting with AI models. +

+ +
+
+ ) : ( +
+ {/* Error Display */} + {error && ( +
+
+ + {error} +
+
+ )} + + {/* Controls */} +
+ {/* Provider Selection */} +
+ +
+ +
+
+ + {/* Model Selection - Only show if provider is selected */} + {selectedProvider && ( +
+ + {useComparisonMode && availableModels.length > 1 && ( + + )} +
+ )} +
+ + {/* Chat Containers */} + {selectedProvider && selectedModel1 && ( +
+
+
+

{selectedModel1}

+
+
+ +
+
+ + {useComparisonMode && selectedModel2 && ( +
+
+

{selectedModel2}

+
+
+ +
+
+ )} +
+ )} + + {/* No Provider Selected State */} + {!selectedProvider && hasConfiguredProviders && ( +
+
+ +

Select a Provider

+

+ Please select an AI provider to start chatting. +

+
+
+ )} + + {/* Chat Input */} + +
+ )} +
+ + {/* API Key Modal */} + setShowApiKeyModal(false)} + onSave={handleSaveApiConfig} + initialConfig={apiConfig} + /> +
+ ); +} + +export default App; \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/src/components/ApiKeyModal.tsx b/aisuite-js/examples/chat-app/src/components/ApiKeyModal.tsx new file mode 100644 index 0000000..6f32bc2 --- /dev/null +++ b/aisuite-js/examples/chat-app/src/components/ApiKeyModal.tsx @@ -0,0 +1,169 @@ +import React, { useState } from 'react'; +import { X, Eye, EyeOff } from 'lucide-react'; +import { AISuiteConfig } from '../types/chat'; + +interface ApiKeyModalProps { + isOpen: boolean; + onClose: () => void; + onSave: (config: AISuiteConfig) => void; + initialConfig?: AISuiteConfig; +} + +export const ApiKeyModal: React.FC = ({ + isOpen, + onClose, + onSave, + initialConfig = {} +}) => { + const [config, setConfig] = useState(initialConfig); + const [showKeys, setShowKeys] = useState>({}); + + const toggleKeyVisibility = (provider: string) => { + setShowKeys(prev => ({ + ...prev, + [provider]: !prev[provider] + })); + }; + + const handleSave = () => { + // Filter out empty API keys + const filteredConfig: AISuiteConfig = {}; + Object.entries(config).forEach(([provider, providerConfig]) => { + if (providerConfig?.apiKey?.trim()) { + providerConfig.dangerouslyAllowBrowser = true; + filteredConfig[provider as keyof AISuiteConfig] = providerConfig; + } + }); + onSave(filteredConfig); + onClose(); + }; + + const updateConfig = (provider: string, field: string, value: string) => { + setConfig(prev => ({ + ...prev, + [provider]: { + ...prev[provider as keyof AISuiteConfig], + [field]: value + } + })); + }; + + if (!isOpen) return null; + + return ( +
+
+
+

Configure API Keys

+ +
+ +
+ {/* OpenAI */} +
+ +
+ updateConfig('openai', 'apiKey', e.target.value)} + className="w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" + /> + +
+
+ + {/* Anthropic */} +
+ +
+ updateConfig('anthropic', 'apiKey', e.target.value)} + className="w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" + /> + +
+
+ + {/* Groq */} +
+ +
+ updateConfig('groq', 'apiKey', e.target.value)} + className="w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" + /> + +
+
+ + {/* Mistral */} +
+ +
+ updateConfig('mistral', 'apiKey', e.target.value)} + className="w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" + /> + +
+
+
+ +
+ + +
+
+
+ ); +}; \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/src/components/ChatContainer.tsx b/aisuite-js/examples/chat-app/src/components/ChatContainer.tsx new file mode 100644 index 0000000..976141e --- /dev/null +++ b/aisuite-js/examples/chat-app/src/components/ChatContainer.tsx @@ -0,0 +1,75 @@ +import React, { useRef, useEffect } from 'react'; +import { Message } from '../types/chat'; +import { ChatMessage } from './ChatMessage'; + +interface ChatContainerProps { + messages: Message[]; + modelName: string; + isLoading?: boolean; +} + +export const ChatContainer: React.FC = ({ + messages, + modelName, + isLoading = false +}) => { + const messagesEndRef = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages]); + + return ( +
+
+
+ {messages.length === 0 ? ( +
+
+
No messages yet
+
Start a conversation with {modelName}
+
+
+ ) : ( + messages.map((message, index) => ( + + )) + )} + + {isLoading && ( +
+
+
+
+
+
+
+ {modelName} +
+
+
+
+
+
+
+ Thinking... +
+
+
+
+ )} + +
+
+
+
+ ); +}; \ No newline at end of file diff --git a/aisuite-js/examples/chat-app/src/components/ChatInput.tsx b/aisuite-js/examples/chat-app/src/components/ChatInput.tsx new file mode 100644 index 0000000..2e57daa --- /dev/null +++ b/aisuite-js/examples/chat-app/src/components/ChatInput.tsx @@ -0,0 +1,79 @@ +import React, { useState, KeyboardEvent } from 'react'; +import { Send, RotateCcw } from 'lucide-react'; + +interface ChatInputProps { + onSendMessage: (message: string) => void; + onResetChat: () => void; + isLoading: boolean; + placeholder?: string; + disabled?: boolean; +} + +export const ChatInput: React.FC = ({ + onSendMessage, + onResetChat, + isLoading, + placeholder = "Enter your query...", + disabled = false +}) => { + const [message, setMessage] = useState(''); + + const handleSend = () => { + if (message.trim() && !isLoading && !disabled) { + onSendMessage(message.trim()); + setMessage(''); + } + }; + + const handleKeyPress = (e: KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + return ( +
+
+
+