ai-parrot 0.17.2__cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentui/.prettierrc +15 -0
- agentui/QUICKSTART.md +272 -0
- agentui/README.md +59 -0
- agentui/env.example +16 -0
- agentui/jsconfig.json +14 -0
- agentui/package-lock.json +4242 -0
- agentui/package.json +34 -0
- agentui/scripts/postinstall/apply-patches.mjs +260 -0
- agentui/src/app.css +61 -0
- agentui/src/app.d.ts +13 -0
- agentui/src/app.html +12 -0
- agentui/src/components/LoadingSpinner.svelte +64 -0
- agentui/src/components/ThemeSwitcher.svelte +159 -0
- agentui/src/components/index.js +4 -0
- agentui/src/lib/api/bots.ts +60 -0
- agentui/src/lib/api/chat.ts +22 -0
- agentui/src/lib/api/http.ts +25 -0
- agentui/src/lib/components/BotCard.svelte +33 -0
- agentui/src/lib/components/ChatBubble.svelte +63 -0
- agentui/src/lib/components/Toast.svelte +21 -0
- agentui/src/lib/config.ts +20 -0
- agentui/src/lib/stores/auth.svelte.ts +73 -0
- agentui/src/lib/stores/theme.svelte.js +64 -0
- agentui/src/lib/stores/toast.svelte.ts +31 -0
- agentui/src/lib/utils/conversation.ts +39 -0
- agentui/src/routes/+layout.svelte +20 -0
- agentui/src/routes/+page.svelte +232 -0
- agentui/src/routes/login/+page.svelte +200 -0
- agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
- agentui/src/routes/talk/[agentId]/+page.ts +7 -0
- agentui/static/README.md +1 -0
- agentui/svelte.config.js +11 -0
- agentui/tailwind.config.ts +53 -0
- agentui/tsconfig.json +3 -0
- agentui/vite.config.ts +10 -0
- ai_parrot-0.17.2.dist-info/METADATA +472 -0
- ai_parrot-0.17.2.dist-info/RECORD +535 -0
- ai_parrot-0.17.2.dist-info/WHEEL +6 -0
- ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
- ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
- ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
- crew-builder/.prettierrc +15 -0
- crew-builder/QUICKSTART.md +259 -0
- crew-builder/README.md +113 -0
- crew-builder/env.example +17 -0
- crew-builder/jsconfig.json +14 -0
- crew-builder/package-lock.json +4182 -0
- crew-builder/package.json +37 -0
- crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
- crew-builder/src/app.css +62 -0
- crew-builder/src/app.d.ts +13 -0
- crew-builder/src/app.html +12 -0
- crew-builder/src/components/LoadingSpinner.svelte +64 -0
- crew-builder/src/components/ThemeSwitcher.svelte +149 -0
- crew-builder/src/components/index.js +9 -0
- crew-builder/src/lib/api/bots.ts +60 -0
- crew-builder/src/lib/api/chat.ts +80 -0
- crew-builder/src/lib/api/client.ts +56 -0
- crew-builder/src/lib/api/crew/crew.ts +136 -0
- crew-builder/src/lib/api/index.ts +5 -0
- crew-builder/src/lib/api/o365/auth.ts +65 -0
- crew-builder/src/lib/auth/auth.ts +54 -0
- crew-builder/src/lib/components/AgentNode.svelte +43 -0
- crew-builder/src/lib/components/BotCard.svelte +33 -0
- crew-builder/src/lib/components/ChatBubble.svelte +67 -0
- crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
- crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
- crew-builder/src/lib/components/JsonViewer.svelte +24 -0
- crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
- crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
- crew-builder/src/lib/components/Toast.svelte +67 -0
- crew-builder/src/lib/components/Toolbar.svelte +157 -0
- crew-builder/src/lib/components/index.ts +10 -0
- crew-builder/src/lib/config.ts +8 -0
- crew-builder/src/lib/stores/auth.svelte.ts +228 -0
- crew-builder/src/lib/stores/crewStore.ts +369 -0
- crew-builder/src/lib/stores/theme.svelte.js +145 -0
- crew-builder/src/lib/stores/toast.svelte.ts +69 -0
- crew-builder/src/lib/utils/conversation.ts +39 -0
- crew-builder/src/lib/utils/markdown.ts +122 -0
- crew-builder/src/lib/utils/talkHistory.ts +47 -0
- crew-builder/src/routes/+layout.svelte +20 -0
- crew-builder/src/routes/+page.svelte +539 -0
- crew-builder/src/routes/agents/+page.svelte +247 -0
- crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
- crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
- crew-builder/src/routes/builder/+page.svelte +204 -0
- crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
- crew-builder/src/routes/crew/ask/+page.ts +1 -0
- crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
- crew-builder/src/routes/login/+page.svelte +197 -0
- crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
- crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
- crew-builder/static/README.md +1 -0
- crew-builder/svelte.config.js +11 -0
- crew-builder/tailwind.config.ts +53 -0
- crew-builder/tsconfig.json +3 -0
- crew-builder/vite.config.ts +10 -0
- mcp_servers/calculator_server.py +309 -0
- parrot/__init__.py +27 -0
- parrot/__pycache__/__init__.cpython-310.pyc +0 -0
- parrot/__pycache__/version.cpython-310.pyc +0 -0
- parrot/_version.py +34 -0
- parrot/a2a/__init__.py +48 -0
- parrot/a2a/client.py +658 -0
- parrot/a2a/discovery.py +89 -0
- parrot/a2a/mixin.py +257 -0
- parrot/a2a/models.py +376 -0
- parrot/a2a/server.py +770 -0
- parrot/agents/__init__.py +29 -0
- parrot/bots/__init__.py +12 -0
- parrot/bots/a2a_agent.py +19 -0
- parrot/bots/abstract.py +3139 -0
- parrot/bots/agent.py +1129 -0
- parrot/bots/basic.py +9 -0
- parrot/bots/chatbot.py +669 -0
- parrot/bots/data.py +1618 -0
- parrot/bots/database/__init__.py +5 -0
- parrot/bots/database/abstract.py +3071 -0
- parrot/bots/database/cache.py +286 -0
- parrot/bots/database/models.py +468 -0
- parrot/bots/database/prompts.py +154 -0
- parrot/bots/database/retries.py +98 -0
- parrot/bots/database/router.py +269 -0
- parrot/bots/database/sql.py +41 -0
- parrot/bots/db/__init__.py +6 -0
- parrot/bots/db/abstract.py +556 -0
- parrot/bots/db/bigquery.py +602 -0
- parrot/bots/db/cache.py +85 -0
- parrot/bots/db/documentdb.py +668 -0
- parrot/bots/db/elastic.py +1014 -0
- parrot/bots/db/influx.py +898 -0
- parrot/bots/db/mock.py +96 -0
- parrot/bots/db/multi.py +783 -0
- parrot/bots/db/prompts.py +185 -0
- parrot/bots/db/sql.py +1255 -0
- parrot/bots/db/tools.py +212 -0
- parrot/bots/document.py +680 -0
- parrot/bots/hrbot.py +15 -0
- parrot/bots/kb.py +170 -0
- parrot/bots/mcp.py +36 -0
- parrot/bots/orchestration/README.md +463 -0
- parrot/bots/orchestration/__init__.py +1 -0
- parrot/bots/orchestration/agent.py +155 -0
- parrot/bots/orchestration/crew.py +3330 -0
- parrot/bots/orchestration/fsm.py +1179 -0
- parrot/bots/orchestration/hr.py +434 -0
- parrot/bots/orchestration/storage/__init__.py +4 -0
- parrot/bots/orchestration/storage/memory.py +100 -0
- parrot/bots/orchestration/storage/mixin.py +119 -0
- parrot/bots/orchestration/verify.py +202 -0
- parrot/bots/product.py +204 -0
- parrot/bots/prompts/__init__.py +96 -0
- parrot/bots/prompts/agents.py +155 -0
- parrot/bots/prompts/data.py +216 -0
- parrot/bots/prompts/output_generation.py +8 -0
- parrot/bots/scraper/__init__.py +3 -0
- parrot/bots/scraper/models.py +122 -0
- parrot/bots/scraper/scraper.py +1173 -0
- parrot/bots/scraper/templates.py +115 -0
- parrot/bots/stores/__init__.py +5 -0
- parrot/bots/stores/local.py +172 -0
- parrot/bots/webdev.py +81 -0
- parrot/cli.py +17 -0
- parrot/clients/__init__.py +16 -0
- parrot/clients/base.py +1491 -0
- parrot/clients/claude.py +1191 -0
- parrot/clients/factory.py +129 -0
- parrot/clients/google.py +4567 -0
- parrot/clients/gpt.py +1975 -0
- parrot/clients/grok.py +432 -0
- parrot/clients/groq.py +986 -0
- parrot/clients/hf.py +582 -0
- parrot/clients/models.py +18 -0
- parrot/conf.py +395 -0
- parrot/embeddings/__init__.py +9 -0
- parrot/embeddings/base.py +157 -0
- parrot/embeddings/google.py +98 -0
- parrot/embeddings/huggingface.py +74 -0
- parrot/embeddings/openai.py +84 -0
- parrot/embeddings/processor.py +88 -0
- parrot/exceptions.c +13868 -0
- parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/exceptions.pxd +22 -0
- parrot/exceptions.pxi +15 -0
- parrot/exceptions.pyx +44 -0
- parrot/generators/__init__.py +29 -0
- parrot/generators/base.py +200 -0
- parrot/generators/html.py +293 -0
- parrot/generators/react.py +205 -0
- parrot/generators/streamlit.py +203 -0
- parrot/generators/template.py +105 -0
- parrot/handlers/__init__.py +4 -0
- parrot/handlers/agent.py +861 -0
- parrot/handlers/agents/__init__.py +1 -0
- parrot/handlers/agents/abstract.py +900 -0
- parrot/handlers/bots.py +338 -0
- parrot/handlers/chat.py +915 -0
- parrot/handlers/creation.sql +192 -0
- parrot/handlers/crew/ARCHITECTURE.md +362 -0
- parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
- parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
- parrot/handlers/crew/__init__.py +0 -0
- parrot/handlers/crew/handler.py +801 -0
- parrot/handlers/crew/models.py +229 -0
- parrot/handlers/crew/redis_persistence.py +523 -0
- parrot/handlers/jobs/__init__.py +10 -0
- parrot/handlers/jobs/job.py +384 -0
- parrot/handlers/jobs/mixin.py +627 -0
- parrot/handlers/jobs/models.py +115 -0
- parrot/handlers/jobs/worker.py +31 -0
- parrot/handlers/models.py +596 -0
- parrot/handlers/o365_auth.py +105 -0
- parrot/handlers/stream.py +337 -0
- parrot/interfaces/__init__.py +6 -0
- parrot/interfaces/aws.py +143 -0
- parrot/interfaces/credentials.py +113 -0
- parrot/interfaces/database.py +27 -0
- parrot/interfaces/google.py +1123 -0
- parrot/interfaces/hierarchy.py +1227 -0
- parrot/interfaces/http.py +651 -0
- parrot/interfaces/images/__init__.py +0 -0
- parrot/interfaces/images/plugins/__init__.py +24 -0
- parrot/interfaces/images/plugins/abstract.py +58 -0
- parrot/interfaces/images/plugins/analisys.py +148 -0
- parrot/interfaces/images/plugins/classify.py +150 -0
- parrot/interfaces/images/plugins/classifybase.py +182 -0
- parrot/interfaces/images/plugins/detect.py +150 -0
- parrot/interfaces/images/plugins/exif.py +1103 -0
- parrot/interfaces/images/plugins/hash.py +52 -0
- parrot/interfaces/images/plugins/vision.py +104 -0
- parrot/interfaces/images/plugins/yolo.py +66 -0
- parrot/interfaces/images/plugins/zerodetect.py +197 -0
- parrot/interfaces/o365.py +978 -0
- parrot/interfaces/onedrive.py +822 -0
- parrot/interfaces/sharepoint.py +1435 -0
- parrot/interfaces/soap.py +257 -0
- parrot/loaders/__init__.py +8 -0
- parrot/loaders/abstract.py +1131 -0
- parrot/loaders/audio.py +199 -0
- parrot/loaders/basepdf.py +53 -0
- parrot/loaders/basevideo.py +1568 -0
- parrot/loaders/csv.py +409 -0
- parrot/loaders/docx.py +116 -0
- parrot/loaders/epubloader.py +316 -0
- parrot/loaders/excel.py +199 -0
- parrot/loaders/factory.py +55 -0
- parrot/loaders/files/__init__.py +0 -0
- parrot/loaders/files/abstract.py +39 -0
- parrot/loaders/files/html.py +26 -0
- parrot/loaders/files/text.py +63 -0
- parrot/loaders/html.py +152 -0
- parrot/loaders/markdown.py +442 -0
- parrot/loaders/pdf.py +373 -0
- parrot/loaders/pdfmark.py +320 -0
- parrot/loaders/pdftables.py +506 -0
- parrot/loaders/ppt.py +476 -0
- parrot/loaders/qa.py +63 -0
- parrot/loaders/splitters/__init__.py +10 -0
- parrot/loaders/splitters/base.py +138 -0
- parrot/loaders/splitters/md.py +228 -0
- parrot/loaders/splitters/token.py +143 -0
- parrot/loaders/txt.py +26 -0
- parrot/loaders/video.py +89 -0
- parrot/loaders/videolocal.py +218 -0
- parrot/loaders/videounderstanding.py +377 -0
- parrot/loaders/vimeo.py +167 -0
- parrot/loaders/web.py +599 -0
- parrot/loaders/youtube.py +504 -0
- parrot/manager/__init__.py +5 -0
- parrot/manager/manager.py +1030 -0
- parrot/mcp/__init__.py +28 -0
- parrot/mcp/adapter.py +105 -0
- parrot/mcp/cli.py +174 -0
- parrot/mcp/client.py +119 -0
- parrot/mcp/config.py +75 -0
- parrot/mcp/integration.py +842 -0
- parrot/mcp/oauth.py +933 -0
- parrot/mcp/server.py +225 -0
- parrot/mcp/transports/__init__.py +3 -0
- parrot/mcp/transports/base.py +279 -0
- parrot/mcp/transports/grpc_session.py +163 -0
- parrot/mcp/transports/http.py +312 -0
- parrot/mcp/transports/mcp.proto +108 -0
- parrot/mcp/transports/quic.py +1082 -0
- parrot/mcp/transports/sse.py +330 -0
- parrot/mcp/transports/stdio.py +309 -0
- parrot/mcp/transports/unix.py +395 -0
- parrot/mcp/transports/websocket.py +547 -0
- parrot/memory/__init__.py +16 -0
- parrot/memory/abstract.py +209 -0
- parrot/memory/agent.py +32 -0
- parrot/memory/cache.py +175 -0
- parrot/memory/core.py +555 -0
- parrot/memory/file.py +153 -0
- parrot/memory/mem.py +131 -0
- parrot/memory/redis.py +613 -0
- parrot/models/__init__.py +46 -0
- parrot/models/basic.py +118 -0
- parrot/models/compliance.py +208 -0
- parrot/models/crew.py +395 -0
- parrot/models/detections.py +654 -0
- parrot/models/generation.py +85 -0
- parrot/models/google.py +223 -0
- parrot/models/groq.py +23 -0
- parrot/models/openai.py +30 -0
- parrot/models/outputs.py +285 -0
- parrot/models/responses.py +938 -0
- parrot/notifications/__init__.py +743 -0
- parrot/openapi/__init__.py +3 -0
- parrot/openapi/components.yaml +641 -0
- parrot/openapi/config.py +322 -0
- parrot/outputs/__init__.py +32 -0
- parrot/outputs/formats/__init__.py +108 -0
- parrot/outputs/formats/altair.py +359 -0
- parrot/outputs/formats/application.py +122 -0
- parrot/outputs/formats/base.py +351 -0
- parrot/outputs/formats/bokeh.py +356 -0
- parrot/outputs/formats/card.py +424 -0
- parrot/outputs/formats/chart.py +436 -0
- parrot/outputs/formats/d3.py +255 -0
- parrot/outputs/formats/echarts.py +310 -0
- parrot/outputs/formats/generators/__init__.py +0 -0
- parrot/outputs/formats/generators/abstract.py +61 -0
- parrot/outputs/formats/generators/panel.py +145 -0
- parrot/outputs/formats/generators/streamlit.py +86 -0
- parrot/outputs/formats/generators/terminal.py +63 -0
- parrot/outputs/formats/holoviews.py +310 -0
- parrot/outputs/formats/html.py +147 -0
- parrot/outputs/formats/jinja2.py +46 -0
- parrot/outputs/formats/json.py +87 -0
- parrot/outputs/formats/map.py +933 -0
- parrot/outputs/formats/markdown.py +172 -0
- parrot/outputs/formats/matplotlib.py +237 -0
- parrot/outputs/formats/mixins/__init__.py +0 -0
- parrot/outputs/formats/mixins/emaps.py +855 -0
- parrot/outputs/formats/plotly.py +341 -0
- parrot/outputs/formats/seaborn.py +310 -0
- parrot/outputs/formats/table.py +397 -0
- parrot/outputs/formats/template_report.py +138 -0
- parrot/outputs/formats/yaml.py +125 -0
- parrot/outputs/formatter.py +152 -0
- parrot/outputs/templates/__init__.py +95 -0
- parrot/pipelines/__init__.py +0 -0
- parrot/pipelines/abstract.py +210 -0
- parrot/pipelines/detector.py +124 -0
- parrot/pipelines/models.py +90 -0
- parrot/pipelines/planogram.py +3002 -0
- parrot/pipelines/table.sql +97 -0
- parrot/plugins/__init__.py +106 -0
- parrot/plugins/importer.py +80 -0
- parrot/py.typed +0 -0
- parrot/registry/__init__.py +18 -0
- parrot/registry/registry.py +594 -0
- parrot/scheduler/__init__.py +1189 -0
- parrot/scheduler/models.py +60 -0
- parrot/security/__init__.py +16 -0
- parrot/security/prompt_injection.py +268 -0
- parrot/security/security_events.sql +25 -0
- parrot/services/__init__.py +1 -0
- parrot/services/mcp/__init__.py +8 -0
- parrot/services/mcp/config.py +13 -0
- parrot/services/mcp/server.py +295 -0
- parrot/services/o365_remote_auth.py +235 -0
- parrot/stores/__init__.py +7 -0
- parrot/stores/abstract.py +352 -0
- parrot/stores/arango.py +1090 -0
- parrot/stores/bigquery.py +1377 -0
- parrot/stores/cache.py +106 -0
- parrot/stores/empty.py +10 -0
- parrot/stores/faiss_store.py +1157 -0
- parrot/stores/kb/__init__.py +9 -0
- parrot/stores/kb/abstract.py +68 -0
- parrot/stores/kb/cache.py +165 -0
- parrot/stores/kb/doc.py +325 -0
- parrot/stores/kb/hierarchy.py +346 -0
- parrot/stores/kb/local.py +457 -0
- parrot/stores/kb/prompt.py +28 -0
- parrot/stores/kb/redis.py +659 -0
- parrot/stores/kb/store.py +115 -0
- parrot/stores/kb/user.py +374 -0
- parrot/stores/models.py +59 -0
- parrot/stores/pgvector.py +3 -0
- parrot/stores/postgres.py +2853 -0
- parrot/stores/utils/__init__.py +0 -0
- parrot/stores/utils/chunking.py +197 -0
- parrot/telemetry/__init__.py +3 -0
- parrot/telemetry/mixin.py +111 -0
- parrot/template/__init__.py +3 -0
- parrot/template/engine.py +259 -0
- parrot/tools/__init__.py +23 -0
- parrot/tools/abstract.py +644 -0
- parrot/tools/agent.py +363 -0
- parrot/tools/arangodbsearch.py +537 -0
- parrot/tools/arxiv_tool.py +188 -0
- parrot/tools/calculator/__init__.py +3 -0
- parrot/tools/calculator/operations/__init__.py +38 -0
- parrot/tools/calculator/operations/calculus.py +80 -0
- parrot/tools/calculator/operations/statistics.py +76 -0
- parrot/tools/calculator/tool.py +150 -0
- parrot/tools/cloudwatch.py +988 -0
- parrot/tools/codeinterpreter/__init__.py +127 -0
- parrot/tools/codeinterpreter/executor.py +371 -0
- parrot/tools/codeinterpreter/internals.py +473 -0
- parrot/tools/codeinterpreter/models.py +643 -0
- parrot/tools/codeinterpreter/prompts.py +224 -0
- parrot/tools/codeinterpreter/tool.py +664 -0
- parrot/tools/company_info/__init__.py +6 -0
- parrot/tools/company_info/tool.py +1138 -0
- parrot/tools/correlationanalysis.py +437 -0
- parrot/tools/database/abstract.py +286 -0
- parrot/tools/database/bq.py +115 -0
- parrot/tools/database/cache.py +284 -0
- parrot/tools/database/models.py +95 -0
- parrot/tools/database/pg.py +343 -0
- parrot/tools/databasequery.py +1159 -0
- parrot/tools/db.py +1800 -0
- parrot/tools/ddgo.py +370 -0
- parrot/tools/decorators.py +271 -0
- parrot/tools/dftohtml.py +282 -0
- parrot/tools/document.py +549 -0
- parrot/tools/ecs.py +819 -0
- parrot/tools/edareport.py +368 -0
- parrot/tools/elasticsearch.py +1049 -0
- parrot/tools/employees.py +462 -0
- parrot/tools/epson/__init__.py +96 -0
- parrot/tools/excel.py +683 -0
- parrot/tools/file/__init__.py +13 -0
- parrot/tools/file/abstract.py +76 -0
- parrot/tools/file/gcs.py +378 -0
- parrot/tools/file/local.py +284 -0
- parrot/tools/file/s3.py +511 -0
- parrot/tools/file/tmp.py +309 -0
- parrot/tools/file/tool.py +501 -0
- parrot/tools/file_reader.py +129 -0
- parrot/tools/flowtask/__init__.py +19 -0
- parrot/tools/flowtask/tool.py +761 -0
- parrot/tools/gittoolkit.py +508 -0
- parrot/tools/google/__init__.py +18 -0
- parrot/tools/google/base.py +169 -0
- parrot/tools/google/tools.py +1251 -0
- parrot/tools/googlelocation.py +5 -0
- parrot/tools/googleroutes.py +5 -0
- parrot/tools/googlesearch.py +5 -0
- parrot/tools/googlesitesearch.py +5 -0
- parrot/tools/googlevoice.py +2 -0
- parrot/tools/gvoice.py +695 -0
- parrot/tools/ibisworld/README.md +225 -0
- parrot/tools/ibisworld/__init__.py +11 -0
- parrot/tools/ibisworld/tool.py +366 -0
- parrot/tools/jiratoolkit.py +1718 -0
- parrot/tools/manager.py +1098 -0
- parrot/tools/math.py +152 -0
- parrot/tools/metadata.py +476 -0
- parrot/tools/msteams.py +1621 -0
- parrot/tools/msword.py +635 -0
- parrot/tools/multidb.py +580 -0
- parrot/tools/multistoresearch.py +369 -0
- parrot/tools/networkninja.py +167 -0
- parrot/tools/nextstop/__init__.py +4 -0
- parrot/tools/nextstop/base.py +286 -0
- parrot/tools/nextstop/employee.py +733 -0
- parrot/tools/nextstop/store.py +462 -0
- parrot/tools/notification.py +435 -0
- parrot/tools/o365/__init__.py +42 -0
- parrot/tools/o365/base.py +295 -0
- parrot/tools/o365/bundle.py +522 -0
- parrot/tools/o365/events.py +554 -0
- parrot/tools/o365/mail.py +992 -0
- parrot/tools/o365/onedrive.py +497 -0
- parrot/tools/o365/sharepoint.py +641 -0
- parrot/tools/openapi_toolkit.py +904 -0
- parrot/tools/openweather.py +527 -0
- parrot/tools/pdfprint.py +1001 -0
- parrot/tools/powerbi.py +518 -0
- parrot/tools/powerpoint.py +1113 -0
- parrot/tools/pricestool.py +146 -0
- parrot/tools/products/__init__.py +246 -0
- parrot/tools/prophet_tool.py +171 -0
- parrot/tools/pythonpandas.py +630 -0
- parrot/tools/pythonrepl.py +910 -0
- parrot/tools/qsource.py +436 -0
- parrot/tools/querytoolkit.py +395 -0
- parrot/tools/quickeda.py +827 -0
- parrot/tools/resttool.py +553 -0
- parrot/tools/retail/__init__.py +0 -0
- parrot/tools/retail/bby.py +528 -0
- parrot/tools/sandboxtool.py +703 -0
- parrot/tools/sassie/__init__.py +352 -0
- parrot/tools/scraping/__init__.py +7 -0
- parrot/tools/scraping/docs/select.md +466 -0
- parrot/tools/scraping/documentation.md +1278 -0
- parrot/tools/scraping/driver.py +436 -0
- parrot/tools/scraping/models.py +576 -0
- parrot/tools/scraping/options.py +85 -0
- parrot/tools/scraping/orchestrator.py +517 -0
- parrot/tools/scraping/readme.md +740 -0
- parrot/tools/scraping/tool.py +3115 -0
- parrot/tools/seasonaldetection.py +642 -0
- parrot/tools/shell_tool/__init__.py +5 -0
- parrot/tools/shell_tool/actions.py +408 -0
- parrot/tools/shell_tool/engine.py +155 -0
- parrot/tools/shell_tool/models.py +322 -0
- parrot/tools/shell_tool/tool.py +442 -0
- parrot/tools/site_search.py +214 -0
- parrot/tools/textfile.py +418 -0
- parrot/tools/think.py +378 -0
- parrot/tools/toolkit.py +298 -0
- parrot/tools/webapp_tool.py +187 -0
- parrot/tools/whatif.py +1279 -0
- parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
- parrot/tools/workday/__init__.py +6 -0
- parrot/tools/workday/models.py +1389 -0
- parrot/tools/workday/tool.py +1293 -0
- parrot/tools/yfinance_tool.py +306 -0
- parrot/tools/zipcode.py +217 -0
- parrot/utils/__init__.py +2 -0
- parrot/utils/helpers.py +73 -0
- parrot/utils/parsers/__init__.py +5 -0
- parrot/utils/parsers/toml.c +12078 -0
- parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/parsers/toml.pyx +21 -0
- parrot/utils/toml.py +11 -0
- parrot/utils/types.cpp +20936 -0
- parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/types.pyx +213 -0
- parrot/utils/uv.py +11 -0
- parrot/version.py +10 -0
- parrot/yaml-rs/Cargo.lock +350 -0
- parrot/yaml-rs/Cargo.toml +19 -0
- parrot/yaml-rs/pyproject.toml +19 -0
- parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
- parrot/yaml-rs/src/lib.rs +222 -0
- requirements/docker-compose.yml +24 -0
- requirements/requirements-dev.txt +21 -0
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
"""
|
|
2
|
+
REST API Handler for AgentCrew Management.
|
|
3
|
+
|
|
4
|
+
Provides endpoints for creating, managing, and executing agent crews.
|
|
5
|
+
|
|
6
|
+
Endpoints:
|
|
7
|
+
PUT /api/v1/crew - Create a new crew
|
|
8
|
+
GET /api/v1/crew - List all crews or get specific crew by name
|
|
9
|
+
POST /api/v1/crew/execute - Execute a crew asynchronously
|
|
10
|
+
PATCH /api/v1/crew/job - Get job status and results
|
|
11
|
+
DELETE /api/v1/crew - Delete a crew
|
|
12
|
+
"""
|
|
13
|
+
from typing import Any, List, Optional
|
|
14
|
+
import uuid
|
|
15
|
+
import json
|
|
16
|
+
from aiohttp import web
|
|
17
|
+
from navigator.views import BaseView
|
|
18
|
+
from navigator.types import WebApp # pylint: disable=E0611,E0401
|
|
19
|
+
from navigator.applications.base import BaseApplication # pylint: disable=E0611,E0401
|
|
20
|
+
from navconfig.logging import logging
|
|
21
|
+
from parrot.bots.orchestration.crew import AgentCrew
|
|
22
|
+
from .models import (
|
|
23
|
+
CrewDefinition,
|
|
24
|
+
JobStatus,
|
|
25
|
+
ExecutionMode,
|
|
26
|
+
)
|
|
27
|
+
from ..jobs import JobManager
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CrewHandler(BaseView):
|
|
31
|
+
"""
|
|
32
|
+
REST API Handler for AgentCrew operations.
|
|
33
|
+
|
|
34
|
+
This handler provides a complete REST API for managing and executing
|
|
35
|
+
agent crews with support for sequential, parallel, and flow-based
|
|
36
|
+
execution modes.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
path: str = '/api/v1/crew'
|
|
40
|
+
app: WebApp = None
|
|
41
|
+
|
|
42
|
+
def __init__(self, *args, **kwargs):
|
|
43
|
+
super().__init__(*args, **kwargs)
|
|
44
|
+
self.logger = logging.getLogger('Parrot.CrewHandler')
|
|
45
|
+
# Get bot manager from app if available
|
|
46
|
+
self._bot_manager = None
|
|
47
|
+
self.job_manager: JobManager = self.app['job_manager'] if 'job_manager' in self.app else JobManager()
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def bot_manager(self):
|
|
51
|
+
"""Get bot manager."""
|
|
52
|
+
if not self._bot_manager:
|
|
53
|
+
app = self.request.app
|
|
54
|
+
self._bot_manager = app['bot_manager'] if 'bot_manager' in app else None
|
|
55
|
+
return self._bot_manager
|
|
56
|
+
|
|
57
|
+
@bot_manager.setter
|
|
58
|
+
def bot_manager(self, value):
|
|
59
|
+
"""Set bot manager."""
|
|
60
|
+
self._bot_manager = value
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
async def configure_job_manager(app: WebApp):
|
|
64
|
+
"""Configure and start job manager."""
|
|
65
|
+
app['job_manager'] = JobManager()
|
|
66
|
+
await app['job_manager'].start()
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def configure(cls, app: WebApp = None, path: str = None, **kwargs) -> WebApp:
|
|
70
|
+
"""configure.
|
|
71
|
+
Configure the CrewHandler in an aiohttp Web Application.
|
|
72
|
+
Args:
|
|
73
|
+
app (WebApp): aiohttp Web Application instance.
|
|
74
|
+
path (str, optional): route path for Model.
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
TypeError: Invalid aiohttp Application.
|
|
78
|
+
ConfigError: Wrong configuration parameters.
|
|
79
|
+
"""
|
|
80
|
+
if isinstance(app, BaseApplication):
|
|
81
|
+
cls.app = app.get_app()
|
|
82
|
+
elif isinstance(app, WebApp):
|
|
83
|
+
cls.app = app # register the app into the Extension
|
|
84
|
+
# startup operations over extension backend
|
|
85
|
+
if app:
|
|
86
|
+
url = f"{path}"
|
|
87
|
+
app.router.add_view(
|
|
88
|
+
r"{url}/{{id:.*}}".format(url=url), cls
|
|
89
|
+
)
|
|
90
|
+
app.router.add_view(
|
|
91
|
+
r"{url}{{meta:(:.*)?}}".format(url=url), cls
|
|
92
|
+
)
|
|
93
|
+
app.on_startup.append(cls.configure_job_manager)
|
|
94
|
+
|
|
95
|
+
async def upload(self):
|
|
96
|
+
"""
|
|
97
|
+
Upload a crew definition from a JSON file.
|
|
98
|
+
|
|
99
|
+
This endpoint accepts multipart/form-data with a JSON file containing
|
|
100
|
+
the crew definition from the visual builder.
|
|
101
|
+
|
|
102
|
+
Form data:
|
|
103
|
+
- file: JSON file with crew definition
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
201: Crew created successfully from file
|
|
107
|
+
400: Invalid file or format
|
|
108
|
+
500: Server error
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
# Get multipart reader
|
|
112
|
+
reader = await self.request.multipart()
|
|
113
|
+
|
|
114
|
+
# Read file field
|
|
115
|
+
field = await reader.next()
|
|
116
|
+
|
|
117
|
+
if not field or field.name != 'file':
|
|
118
|
+
return self.error(
|
|
119
|
+
response={"message": "No file provided. Expected 'file' field."},
|
|
120
|
+
status=400
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Read file content
|
|
124
|
+
content = await field.read(decode=True)
|
|
125
|
+
try:
|
|
126
|
+
crew_data = json.loads(content)
|
|
127
|
+
except json.JSONDecodeError as e:
|
|
128
|
+
return self.error(
|
|
129
|
+
response={"message": f"Invalid JSON format: {str(e)}"},
|
|
130
|
+
status=400
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Validate bot manager availability
|
|
134
|
+
if not self.bot_manager:
|
|
135
|
+
return self.error(
|
|
136
|
+
response={"message": "BotManager not available"},
|
|
137
|
+
status=500
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Parse into CrewDefinition
|
|
141
|
+
try:
|
|
142
|
+
crew_def = CrewDefinition(**crew_data)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
return self.error(
|
|
145
|
+
response={"message": f"Invalid crew definition: {str(e)}"},
|
|
146
|
+
status=400
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Create the crew
|
|
150
|
+
try:
|
|
151
|
+
crew = await self._create_crew_from_definition(crew_def)
|
|
152
|
+
|
|
153
|
+
# Register crew in bot manager
|
|
154
|
+
await self.bot_manager.add_crew(crew_def.name, crew, crew_def)
|
|
155
|
+
|
|
156
|
+
self.logger.info(
|
|
157
|
+
f"Uploaded and created crew '{crew_def.name}' with {len(crew_def.agents)} agents"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
return self.json_response(
|
|
161
|
+
{
|
|
162
|
+
"message": "Crew uploaded and created successfully",
|
|
163
|
+
"crew_id": crew_def.crew_id,
|
|
164
|
+
"name": crew_def.name,
|
|
165
|
+
"execution_mode": crew_def.execution_mode.value, # pylint: disable=E1101 #noqa
|
|
166
|
+
"agents": [agent.agent_id for agent in crew_def.agents],
|
|
167
|
+
"created_at": crew_def.created_at.isoformat()
|
|
168
|
+
},
|
|
169
|
+
status=201
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
except Exception as e:
|
|
173
|
+
self.logger.error(f"Error creating crew from upload: {e}", exc_info=True)
|
|
174
|
+
return self.error(
|
|
175
|
+
response={"message": f"Error creating crew: {str(e)}"},
|
|
176
|
+
status=400
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
except web.HTTPError:
|
|
180
|
+
raise
|
|
181
|
+
except Exception as e:
|
|
182
|
+
self.logger.error(f"Error processing upload: {e}", exc_info=True)
|
|
183
|
+
return self.error(
|
|
184
|
+
response={"message": f"Error processing upload: {str(e)}"},
|
|
185
|
+
status=500
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
async def put(self):
|
|
189
|
+
"""
|
|
190
|
+
Create a new AgentCrew or update an existing one.
|
|
191
|
+
|
|
192
|
+
URL parameters:
|
|
193
|
+
- id: Crew ID or name (optional, for updates)
|
|
194
|
+
e.g., /api/v1/crew/my-crew-id
|
|
195
|
+
|
|
196
|
+
Request body should contain CrewDefinition:
|
|
197
|
+
{
|
|
198
|
+
"name": "research_crew",
|
|
199
|
+
"execution_mode": "sequential|parallel|flow",
|
|
200
|
+
"agents": [
|
|
201
|
+
{
|
|
202
|
+
"agent_id": "researcher",
|
|
203
|
+
"agent_class": "BaseAgent",
|
|
204
|
+
"name": "Research Agent",
|
|
205
|
+
"config": {"model": "gpt-4", "temperature": 0.7},
|
|
206
|
+
"tools": ["web_search"],
|
|
207
|
+
"system_prompt": "You are a researcher..."
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"flow_relations": [ // Only for flow mode
|
|
211
|
+
{"source": "agent1", "target": ["agent2", "agent3"]},
|
|
212
|
+
{"source": ["agent2", "agent3"], "target": "agent4"}
|
|
213
|
+
],
|
|
214
|
+
"shared_tools": ["calculator"],
|
|
215
|
+
"max_parallel_tasks": 10
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
201: Crew created successfully
|
|
220
|
+
200: Crew updated successfully
|
|
221
|
+
400: Invalid request
|
|
222
|
+
404: Crew not found (for updates)
|
|
223
|
+
500: Server error
|
|
224
|
+
"""
|
|
225
|
+
try:
|
|
226
|
+
# Get crew ID from URL if provided
|
|
227
|
+
match_params = self.match_parameters(self.request)
|
|
228
|
+
url_crew_id = match_params.get('id')
|
|
229
|
+
|
|
230
|
+
# Parse request body
|
|
231
|
+
data = await self.request.json()
|
|
232
|
+
crew_def = CrewDefinition(**data)
|
|
233
|
+
|
|
234
|
+
# Validate bot manager availability
|
|
235
|
+
if not self.bot_manager:
|
|
236
|
+
return self.error(
|
|
237
|
+
response={
|
|
238
|
+
"message": "BotManager not available"
|
|
239
|
+
},
|
|
240
|
+
status=500
|
|
241
|
+
)
|
|
242
|
+
# if crew_id is provided, then is an update
|
|
243
|
+
if url_crew_id:
|
|
244
|
+
existing_crew = await self.bot_manager.get_crew(url_crew_id)
|
|
245
|
+
if not existing_crew:
|
|
246
|
+
return self.error(
|
|
247
|
+
response={
|
|
248
|
+
"message": f"Crew '{url_crew_id}' not found for update"
|
|
249
|
+
},
|
|
250
|
+
status=404
|
|
251
|
+
)
|
|
252
|
+
# Update existing crew definition
|
|
253
|
+
_, existing_def = existing_crew
|
|
254
|
+
crew_def.crew_id = existing_def.crew_id # Preserve original ID
|
|
255
|
+
crew_def.created_at = existing_def.created_at # Preserve creation time
|
|
256
|
+
crew_def.updated_at = None # Will be set on save
|
|
257
|
+
|
|
258
|
+
# Remove old crew
|
|
259
|
+
await self.bot_manager.remove_crew(url_crew_id)
|
|
260
|
+
|
|
261
|
+
self.logger.info(f"Updating crew '{url_crew_id}'")
|
|
262
|
+
|
|
263
|
+
# Create the crew via bot manager
|
|
264
|
+
try:
|
|
265
|
+
crew = await self._create_crew_from_definition(crew_def)
|
|
266
|
+
|
|
267
|
+
crew_key = url_crew_id or crew_def.name
|
|
268
|
+
|
|
269
|
+
# Register crew in bot manager
|
|
270
|
+
await self.bot_manager.add_crew(crew_key, crew, crew_def)
|
|
271
|
+
|
|
272
|
+
action = "updated" if url_crew_id else "created"
|
|
273
|
+
status_code = 202 if url_crew_id else 201
|
|
274
|
+
|
|
275
|
+
self.logger.info(
|
|
276
|
+
f"{action.capitalize()} crew '{crew_def.name}' with {len(crew_def.agents)} agents"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
return self.json_response(
|
|
280
|
+
{
|
|
281
|
+
"message": f"Crew {action} successfully",
|
|
282
|
+
"crew_id": crew_def.crew_id,
|
|
283
|
+
"name": crew_def.name,
|
|
284
|
+
"execution_mode": crew_def.execution_mode.value, # pylint: disable=E1101
|
|
285
|
+
"agents": [agent.agent_id for agent in crew_def.agents],
|
|
286
|
+
"created_at": crew_def.created_at.isoformat() # pylint: disable=E1101
|
|
287
|
+
},
|
|
288
|
+
status=status_code
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
except Exception as e:
|
|
292
|
+
self.logger.error(f"Error creating crew: {e}", exc_info=True)
|
|
293
|
+
return self.error(
|
|
294
|
+
response={
|
|
295
|
+
"message": f"Error creating crew: {str(e)}"
|
|
296
|
+
},
|
|
297
|
+
status=400
|
|
298
|
+
)
|
|
299
|
+
except web.HTTPError:
|
|
300
|
+
raise
|
|
301
|
+
except Exception as e:
|
|
302
|
+
self.logger.error(f"Error parsing request: {e}", exc_info=True)
|
|
303
|
+
return self.error(
|
|
304
|
+
response={
|
|
305
|
+
"message": f"Invalid request: {str(e)}"
|
|
306
|
+
},
|
|
307
|
+
status=400
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
async def get(self):
|
|
311
|
+
"""
|
|
312
|
+
Get crew information.
|
|
313
|
+
|
|
314
|
+
Query parameters:
|
|
315
|
+
- name: Crew name (optional) - returns specific crew if provided
|
|
316
|
+
- crew_id: Crew ID (optional) - returns specific crew if provided
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
200: Crew definition(s)
|
|
320
|
+
404: Crew not found
|
|
321
|
+
500: Server error
|
|
322
|
+
"""
|
|
323
|
+
try:
|
|
324
|
+
qs = self.get_arguments(self.request)
|
|
325
|
+
match_params = self.match_parameters(self.request)
|
|
326
|
+
crew_id = match_params.get('id') or qs.get('crew_id')
|
|
327
|
+
crew_name = qs.get('name')
|
|
328
|
+
|
|
329
|
+
if not self.bot_manager:
|
|
330
|
+
return self.error(
|
|
331
|
+
response={"message": "BotManager not available"},
|
|
332
|
+
status=400
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Get specific crew
|
|
336
|
+
if crew_name or crew_id:
|
|
337
|
+
identifier = crew_name or crew_id
|
|
338
|
+
crew_data = await self.bot_manager.get_crew(identifier)
|
|
339
|
+
|
|
340
|
+
if not crew_data:
|
|
341
|
+
return self.error(
|
|
342
|
+
response={
|
|
343
|
+
"message": f"Crew '{identifier}' not found"
|
|
344
|
+
},
|
|
345
|
+
status=404
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
crew, crew_def = crew_data
|
|
349
|
+
return self.json_response({
|
|
350
|
+
"crew_id": crew_def.crew_id,
|
|
351
|
+
"name": crew_def.name,
|
|
352
|
+
"description": crew_def.description,
|
|
353
|
+
"execution_mode": crew_def.execution_mode.value,
|
|
354
|
+
"agents": [agent.dict() for agent in crew_def.agents],
|
|
355
|
+
"flow_relations": [
|
|
356
|
+
rel.dict() for rel in crew_def.flow_relations
|
|
357
|
+
],
|
|
358
|
+
"shared_tools": crew_def.shared_tools,
|
|
359
|
+
"max_parallel_tasks": crew_def.max_parallel_tasks,
|
|
360
|
+
"created_at": crew_def.created_at.isoformat(),
|
|
361
|
+
"updated_at": crew_def.updated_at.isoformat(),
|
|
362
|
+
"metadata": crew_def.metadata
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
# List all crews
|
|
366
|
+
crews = self.bot_manager.list_crews()
|
|
367
|
+
crew_list = []
|
|
368
|
+
|
|
369
|
+
crew_list.extend(
|
|
370
|
+
{
|
|
371
|
+
"crew_id": crew_def.crew_id,
|
|
372
|
+
"name": crew_def.name,
|
|
373
|
+
"description": crew_def.description,
|
|
374
|
+
"execution_mode": crew_def.execution_mode.value,
|
|
375
|
+
"agent_count": len(crew_def.agents),
|
|
376
|
+
"created_at": crew_def.created_at.isoformat(),
|
|
377
|
+
}
|
|
378
|
+
for name, (crew, crew_def) in crews.items()
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
return self.json_response({
|
|
382
|
+
"crews": crew_list,
|
|
383
|
+
"total": len(crew_list)
|
|
384
|
+
})
|
|
385
|
+
except web.HTTPError:
|
|
386
|
+
raise
|
|
387
|
+
except Exception as e:
|
|
388
|
+
self.logger.error(f"Error getting crew: {e}", exc_info=True)
|
|
389
|
+
return self.error(
|
|
390
|
+
response={"message": f"Error: {str(e)}"},
|
|
391
|
+
status=500
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
async def post(self):
|
|
395
|
+
"""
|
|
396
|
+
Execute a crew asynchronously.
|
|
397
|
+
|
|
398
|
+
Request body:
|
|
399
|
+
{
|
|
400
|
+
"crew_id": "uuid" or "name": "crew_name",
|
|
401
|
+
"query": "What is the status of AI research?" or {"agent1": "task1", "agent2": "task2"},
|
|
402
|
+
"execution_mode": "sequential|parallel|loop|flow",
|
|
403
|
+
"user_id": "optional_user_id",
|
|
404
|
+
"session_id": "optional_session_id",
|
|
405
|
+
"synthesis_prompt": "optional synthesis prompt for research mode",
|
|
406
|
+
"kwargs": {} // Additional execution arguments
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
202: Job created and queued
|
|
411
|
+
400: Invalid request
|
|
412
|
+
404: Crew not found
|
|
413
|
+
500: Server error
|
|
414
|
+
"""
|
|
415
|
+
try:
|
|
416
|
+
# Parse request
|
|
417
|
+
data = await self.request.json()
|
|
418
|
+
|
|
419
|
+
# Get crew identifier
|
|
420
|
+
crew_id = data.get('crew_id') or data.get('name')
|
|
421
|
+
if not crew_id:
|
|
422
|
+
return self.error(
|
|
423
|
+
response={"message": "crew_id or name is required"},
|
|
424
|
+
status=400
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
query = data.get('query')
|
|
428
|
+
if not query:
|
|
429
|
+
return self.error(
|
|
430
|
+
response={"message": "query is required"},
|
|
431
|
+
status=400
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Get crew
|
|
435
|
+
if not self.bot_manager:
|
|
436
|
+
return self.error(
|
|
437
|
+
response={"message": "BotManager not available"},
|
|
438
|
+
status=500
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
crew_data = await self.bot_manager.get_crew(crew_id)
|
|
442
|
+
if not crew_data:
|
|
443
|
+
return self.error(
|
|
444
|
+
response={"message": f"Crew '{crew_id}' not found"},
|
|
445
|
+
status=404
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
crew, crew_def = crew_data
|
|
449
|
+
requested_mode = data.get('execution_mode')
|
|
450
|
+
override_mode: Optional[ExecutionMode] = None
|
|
451
|
+
if requested_mode:
|
|
452
|
+
try:
|
|
453
|
+
override_mode = ExecutionMode(requested_mode)
|
|
454
|
+
except ValueError:
|
|
455
|
+
return self.error(
|
|
456
|
+
response={"message": f"Invalid execution mode: {requested_mode}"},
|
|
457
|
+
status=400
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
selected_mode = override_mode or crew_def.execution_mode
|
|
461
|
+
# Create a job for async execution
|
|
462
|
+
job_id = str(uuid.uuid4())
|
|
463
|
+
|
|
464
|
+
# Create job
|
|
465
|
+
job = self.job_manager.create_job(
|
|
466
|
+
job_id=job_id,
|
|
467
|
+
obj_id=crew_def.crew_id,
|
|
468
|
+
query=query,
|
|
469
|
+
user_id=data.get('user_id'),
|
|
470
|
+
session_id=data.get('session_id'),
|
|
471
|
+
execution_mode=selected_mode.value
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Execute asynchronously
|
|
475
|
+
execution_kwargs = data.get('kwargs', {})
|
|
476
|
+
synthesis_prompt = data.get('synthesis_prompt', None)
|
|
477
|
+
|
|
478
|
+
execution_kwargs.update({
|
|
479
|
+
'user_id': job.user_id,
|
|
480
|
+
'session_id': job.session_id,
|
|
481
|
+
"max_tokens": execution_kwargs.get("max_tokens", 4096),
|
|
482
|
+
"temperature": execution_kwargs.get("temperature", 0.1)
|
|
483
|
+
})
|
|
484
|
+
if synthesis_prompt:
|
|
485
|
+
execution_kwargs['synthesis_prompt'] = synthesis_prompt
|
|
486
|
+
|
|
487
|
+
async def execute_crew():
|
|
488
|
+
"""Async execution function."""
|
|
489
|
+
try:
|
|
490
|
+
# Determine execution mode
|
|
491
|
+
mode = override_mode or crew_def.execution_mode
|
|
492
|
+
|
|
493
|
+
if mode == ExecutionMode.SEQUENTIAL:
|
|
494
|
+
result = await crew.run_sequential(
|
|
495
|
+
query=query,
|
|
496
|
+
**execution_kwargs
|
|
497
|
+
)
|
|
498
|
+
elif mode == ExecutionMode.PARALLEL:
|
|
499
|
+
# Handle parallel execution
|
|
500
|
+
if isinstance(query, dict):
|
|
501
|
+
tasks = [
|
|
502
|
+
{"agent_id": agent_id, "query": agent_query}
|
|
503
|
+
for agent_id, agent_query in query.items()
|
|
504
|
+
]
|
|
505
|
+
else:
|
|
506
|
+
tasks = [
|
|
507
|
+
{"agent_id": agent_id, "query": query}
|
|
508
|
+
for agent_id in crew.agents.keys()
|
|
509
|
+
]
|
|
510
|
+
|
|
511
|
+
result = await crew.run_parallel(
|
|
512
|
+
tasks=tasks,
|
|
513
|
+
**execution_kwargs
|
|
514
|
+
)
|
|
515
|
+
elif mode == ExecutionMode.LOOP:
|
|
516
|
+
if not isinstance(query, str):
|
|
517
|
+
raise ValueError("Loop execution requires a string query for the initial task")
|
|
518
|
+
|
|
519
|
+
loop_condition = execution_kwargs.pop('condition', None)
|
|
520
|
+
if not loop_condition or not isinstance(loop_condition, str):
|
|
521
|
+
raise ValueError("Loop execution requires a 'condition' string in kwargs")
|
|
522
|
+
|
|
523
|
+
agent_sequence = execution_kwargs.pop('agent_sequence', None)
|
|
524
|
+
if agent_sequence is not None:
|
|
525
|
+
if not isinstance(agent_sequence, list):
|
|
526
|
+
raise ValueError("'agent_sequence' must be a list of agent identifiers")
|
|
527
|
+
if not all(isinstance(agent_id, str) for agent_id in agent_sequence):
|
|
528
|
+
raise ValueError("'agent_sequence' values must be strings")
|
|
529
|
+
|
|
530
|
+
max_iterations = execution_kwargs.pop('max_iterations', None)
|
|
531
|
+
if max_iterations is None:
|
|
532
|
+
max_iterations = 2
|
|
533
|
+
elif not isinstance(max_iterations, int):
|
|
534
|
+
raise ValueError("'max_iterations' must be an integer")
|
|
535
|
+
|
|
536
|
+
result = await crew.run_loop(
|
|
537
|
+
initial_task=query,
|
|
538
|
+
condition=loop_condition,
|
|
539
|
+
agent_sequence=agent_sequence,
|
|
540
|
+
max_iterations=max_iterations,
|
|
541
|
+
**execution_kwargs
|
|
542
|
+
)
|
|
543
|
+
elif mode == ExecutionMode.FLOW:
|
|
544
|
+
result = await crew.run_flow(
|
|
545
|
+
initial_task=query,
|
|
546
|
+
**execution_kwargs
|
|
547
|
+
)
|
|
548
|
+
else:
|
|
549
|
+
raise ValueError(f"Unknown execution mode: {mode}")
|
|
550
|
+
|
|
551
|
+
# Convert CrewResult to dict if necessary
|
|
552
|
+
if hasattr(result, 'to_dict'):
|
|
553
|
+
return result.to_dict()
|
|
554
|
+
elif hasattr(result, '__dict__'):
|
|
555
|
+
return result.__dict__
|
|
556
|
+
else:
|
|
557
|
+
return result
|
|
558
|
+
|
|
559
|
+
except Exception as e:
|
|
560
|
+
self.logger.error(
|
|
561
|
+
f"Error executing crew {crew_id}: {e}",
|
|
562
|
+
exc_info=True
|
|
563
|
+
)
|
|
564
|
+
raise
|
|
565
|
+
|
|
566
|
+
# Start execution
|
|
567
|
+
await self.job_manager.execute_job(
|
|
568
|
+
job.job_id,
|
|
569
|
+
execute_crew
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
# Return job ID for tracking
|
|
573
|
+
return self.json_response(
|
|
574
|
+
{
|
|
575
|
+
"job_id": job.job_id,
|
|
576
|
+
"crew_id": crew_def.crew_id,
|
|
577
|
+
"status": job.status.value,
|
|
578
|
+
"message": "Crew execution started",
|
|
579
|
+
"created_at": job.created_at.isoformat(),
|
|
580
|
+
"execution_mode": selected_mode.value
|
|
581
|
+
},
|
|
582
|
+
status=202
|
|
583
|
+
)
|
|
584
|
+
except web.HTTPError:
|
|
585
|
+
raise
|
|
586
|
+
except Exception as e:
|
|
587
|
+
self.logger.error(f"Error creating job: {e}", exc_info=True)
|
|
588
|
+
return self.error(
|
|
589
|
+
response={"message": f"Error: {str(e)}"},
|
|
590
|
+
status=500
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
async def patch(self):
|
|
594
|
+
"""
|
|
595
|
+
Get job status and results.
|
|
596
|
+
|
|
597
|
+
Query parameters:
|
|
598
|
+
- job_id: Job identifier (required)
|
|
599
|
+
|
|
600
|
+
Returns:
|
|
601
|
+
200: Job status and results if completed
|
|
602
|
+
404: Job not found
|
|
603
|
+
500: Server error
|
|
604
|
+
"""
|
|
605
|
+
try:
|
|
606
|
+
qs = self.get_arguments(self.request)
|
|
607
|
+
match_params = self.match_parameters(self.request)
|
|
608
|
+
job_id = match_params.get('id') or qs.get('job_id')
|
|
609
|
+
if not job_id:
|
|
610
|
+
# get from json body as fallback
|
|
611
|
+
data = await self.request.json()
|
|
612
|
+
job_id = data.get('job_id')
|
|
613
|
+
|
|
614
|
+
if not job_id:
|
|
615
|
+
return self.error(
|
|
616
|
+
response={"message": "job_id is required"},
|
|
617
|
+
status=400
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
# Get job
|
|
621
|
+
job = self.job_manager.get_job(job_id)
|
|
622
|
+
if not job:
|
|
623
|
+
return self.error(
|
|
624
|
+
response={"message": f"Job '{job_id}' not found"},
|
|
625
|
+
status=404
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
# Return job status
|
|
629
|
+
response_data = {
|
|
630
|
+
"job_id": job.job_id,
|
|
631
|
+
"crew_id": job.obj_id,
|
|
632
|
+
"status": job.status.value,
|
|
633
|
+
"elapsed_time": job.elapsed_time,
|
|
634
|
+
"created_at": job.created_at.isoformat(),
|
|
635
|
+
"metadata": job.metadata,
|
|
636
|
+
"execution_mode": job.execution_mode
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
# Add result if completed
|
|
640
|
+
if job.status == JobStatus.COMPLETED:
|
|
641
|
+
response_data["result"] = job.result
|
|
642
|
+
response_data["completed_at"] = job.completed_at.isoformat()
|
|
643
|
+
elif job.status == JobStatus.FAILED:
|
|
644
|
+
response_data["error"] = job.error
|
|
645
|
+
response_data["completed_at"] = job.completed_at.isoformat()
|
|
646
|
+
elif job.status == JobStatus.RUNNING:
|
|
647
|
+
response_data["started_at"] = job.started_at.isoformat()
|
|
648
|
+
|
|
649
|
+
return self.json_response(response_data)
|
|
650
|
+
except web.HTTPError:
|
|
651
|
+
raise
|
|
652
|
+
except Exception as e:
|
|
653
|
+
self.logger.error(f"Error getting job status: {e}", exc_info=True)
|
|
654
|
+
return self.error(
|
|
655
|
+
response={"message": f"Error: {str(e)}"},
|
|
656
|
+
status=500
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
async def delete(self):
|
|
660
|
+
"""
|
|
661
|
+
Delete a crew.
|
|
662
|
+
|
|
663
|
+
Query parameters:
|
|
664
|
+
- name: Crew name (optional)
|
|
665
|
+
- crew_id: Crew ID (optional)
|
|
666
|
+
|
|
667
|
+
Returns:
|
|
668
|
+
200: Crew deleted successfully
|
|
669
|
+
404: Crew not found
|
|
670
|
+
500: Server error
|
|
671
|
+
"""
|
|
672
|
+
try:
|
|
673
|
+
match_params = self.match_parameters(self.request)
|
|
674
|
+
qs = self.get_arguments(self.request)
|
|
675
|
+
crew_id = match_params.get('id') or qs.get('crew_id')
|
|
676
|
+
crew_name = qs.get('name')
|
|
677
|
+
|
|
678
|
+
if not crew_name and not crew_id:
|
|
679
|
+
return self.error(
|
|
680
|
+
response={"message": "name or crew_id is required"},
|
|
681
|
+
status=400
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
if not self.bot_manager:
|
|
685
|
+
return self.error(
|
|
686
|
+
response={"message": "BotManager not available"},
|
|
687
|
+
status=500
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
identifier = crew_name or crew_id
|
|
691
|
+
success = await self.bot_manager.remove_crew(identifier)
|
|
692
|
+
|
|
693
|
+
if success:
|
|
694
|
+
return self.json_response({
|
|
695
|
+
"message": f"Crew '{identifier}' deleted successfully"
|
|
696
|
+
})
|
|
697
|
+
else:
|
|
698
|
+
return self.error(
|
|
699
|
+
response={"message": f"Crew '{identifier}' not found"},
|
|
700
|
+
status=404
|
|
701
|
+
)
|
|
702
|
+
except web.HTTPError:
|
|
703
|
+
raise
|
|
704
|
+
except Exception as e:
|
|
705
|
+
self.logger.error(f"Error deleting crew: {e}", exc_info=True)
|
|
706
|
+
return self.error(
|
|
707
|
+
response={"message": f"Error: {str(e)}"},
|
|
708
|
+
status=500
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
async def _create_crew_from_definition(
|
|
712
|
+
self,
|
|
713
|
+
crew_def: CrewDefinition
|
|
714
|
+
) -> AgentCrew:
|
|
715
|
+
"""
|
|
716
|
+
Create an AgentCrew instance from a CrewDefinition.
|
|
717
|
+
|
|
718
|
+
Args:
|
|
719
|
+
crew_def: Crew definition
|
|
720
|
+
|
|
721
|
+
Returns:
|
|
722
|
+
AgentCrew instance
|
|
723
|
+
"""
|
|
724
|
+
# Create agents
|
|
725
|
+
agents = []
|
|
726
|
+
for agent_def in crew_def.agents:
|
|
727
|
+
# Get agent class
|
|
728
|
+
agent_class = self.bot_manager.get_bot_class(agent_def.agent_class)
|
|
729
|
+
|
|
730
|
+
tools = []
|
|
731
|
+
if agent_def.tools:
|
|
732
|
+
tools.extend(iter(agent_def.tools))
|
|
733
|
+
|
|
734
|
+
# Create agent instance
|
|
735
|
+
agent = agent_class(
|
|
736
|
+
name=agent_def.name or agent_def.agent_id,
|
|
737
|
+
tools=tools,
|
|
738
|
+
**agent_def.config
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
# Set system prompt if provided
|
|
742
|
+
if agent_def.system_prompt:
|
|
743
|
+
agent.system_prompt = agent_def.system_prompt
|
|
744
|
+
|
|
745
|
+
agents.append(agent)
|
|
746
|
+
|
|
747
|
+
# Create crew
|
|
748
|
+
crew = AgentCrew(
|
|
749
|
+
name=crew_def.name,
|
|
750
|
+
agents=agents,
|
|
751
|
+
max_parallel_tasks=crew_def.max_parallel_tasks
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
# Add shared tools
|
|
755
|
+
for tool_name in crew_def.shared_tools:
|
|
756
|
+
if tool := self.bot_manager.get_tool(tool_name):
|
|
757
|
+
crew.add_shared_tool(tool, tool_name)
|
|
758
|
+
|
|
759
|
+
# Setup flow relations if in flow mode
|
|
760
|
+
if crew_def.execution_mode == ExecutionMode.FLOW and crew_def.flow_relations:
|
|
761
|
+
for relation in crew_def.flow_relations:
|
|
762
|
+
# Convert agent IDs to agent objects
|
|
763
|
+
source_agents = self._get_agents_by_ids(
|
|
764
|
+
crew,
|
|
765
|
+
relation.source if isinstance(relation.source, list) else [relation.source]
|
|
766
|
+
)
|
|
767
|
+
target_agents = self._get_agents_by_ids(
|
|
768
|
+
crew,
|
|
769
|
+
relation.target if isinstance(relation.target, list) else [relation.target]
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
# Setup flow
|
|
773
|
+
crew.task_flow(
|
|
774
|
+
source_agents if len(source_agents) > 1 else source_agents[0],
|
|
775
|
+
target_agents if len(target_agents) > 1 else target_agents[0]
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
return crew
|
|
779
|
+
|
|
780
|
+
def _get_agents_by_ids(
|
|
781
|
+
self,
|
|
782
|
+
crew: AgentCrew,
|
|
783
|
+
agent_ids: List[str]
|
|
784
|
+
) -> List[Any]:
|
|
785
|
+
"""
|
|
786
|
+
Get agent objects from crew by their IDs.
|
|
787
|
+
|
|
788
|
+
Args:
|
|
789
|
+
crew: AgentCrew instance
|
|
790
|
+
agent_ids: List of agent IDs
|
|
791
|
+
|
|
792
|
+
Returns:
|
|
793
|
+
List of agent objects
|
|
794
|
+
"""
|
|
795
|
+
agents = []
|
|
796
|
+
for agent_id in agent_ids:
|
|
797
|
+
if agent := crew.agents.get(agent_id):
|
|
798
|
+
agents.append(agent)
|
|
799
|
+
else:
|
|
800
|
+
self.logger.warning(f"Agent '{agent_id}' not found in crew")
|
|
801
|
+
return agents
|