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
parrot/handlers/agent.py
ADDED
|
@@ -0,0 +1,861 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AgentTalk - HTTP Handler for Agent Conversations
|
|
3
|
+
=================================================
|
|
4
|
+
Provides a flexible HTTP interface for talking with agents/bots using the ask() method
|
|
5
|
+
with support for multiple output modes and MCP server integration.
|
|
6
|
+
"""
|
|
7
|
+
from typing import Dict, Any, List, Union
|
|
8
|
+
import tempfile
|
|
9
|
+
import os
|
|
10
|
+
import json
|
|
11
|
+
import inspect
|
|
12
|
+
from aiohttp import web
|
|
13
|
+
import pandas as pd
|
|
14
|
+
from datamodel.parsers.json import json_encoder # noqa pylint: disable=E0611
|
|
15
|
+
from navconfig.logging import logging
|
|
16
|
+
from navigator_auth.decorators import is_authenticated, user_session
|
|
17
|
+
from navigator.views import BaseView
|
|
18
|
+
from ..bots.abstract import AbstractBot
|
|
19
|
+
from ..bots.data import PandasAgent
|
|
20
|
+
from ..models.responses import AIMessage, AgentResponse
|
|
21
|
+
from ..outputs import OutputMode, OutputFormatter
|
|
22
|
+
from ..mcp.integration import MCPServerConfig
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@is_authenticated()
|
|
26
|
+
@user_session()
|
|
27
|
+
class AgentTalk(BaseView):
|
|
28
|
+
"""
|
|
29
|
+
AgentTalk Handler - Universal agent conversation interface.
|
|
30
|
+
|
|
31
|
+
Endpoints:
|
|
32
|
+
POST /api/v1/agents/chat/ - Main chat endpoint with format negotiation
|
|
33
|
+
|
|
34
|
+
Features:
|
|
35
|
+
- POST to /api/v1/agents/chat/ to interact with agents
|
|
36
|
+
- Uses BotManager to retrieve the agent
|
|
37
|
+
- Supports multiple output formats (JSON, HTML, Markdown, Terminal)
|
|
38
|
+
- Can add MCP servers dynamically via POST attributes
|
|
39
|
+
- Leverages OutputMode for consistent formatting
|
|
40
|
+
- Session-based conversation management
|
|
41
|
+
"""
|
|
42
|
+
_logger_name: str = "Parrot.AgentTalk"
|
|
43
|
+
|
|
44
|
+
def post_init(self, *args, **kwargs):
|
|
45
|
+
self.logger = logging.getLogger(self._logger_name)
|
|
46
|
+
self.logger.setLevel(logging.DEBUG)
|
|
47
|
+
|
|
48
|
+
def _get_output_format(
|
|
49
|
+
self,
|
|
50
|
+
data: Dict[str, Any],
|
|
51
|
+
qs: Dict[str, Any]
|
|
52
|
+
) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Determine the output format from request.
|
|
55
|
+
|
|
56
|
+
Priority:
|
|
57
|
+
1. Explicit 'output_format' in request body or query string
|
|
58
|
+
2. Content-Type header from Accept header
|
|
59
|
+
3. Default to 'json'
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
data: Request body data
|
|
63
|
+
qs: Query string parameters
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Output format string: 'json', 'html', 'markdown', or 'text'
|
|
67
|
+
"""
|
|
68
|
+
# Check explicit output_format parameter
|
|
69
|
+
output_format = data.pop('output_format', None) or qs.get('output_format')
|
|
70
|
+
if output_format:
|
|
71
|
+
return output_format.lower()
|
|
72
|
+
|
|
73
|
+
# Check Accept header
|
|
74
|
+
accept_header = self.request.headers.get('Accept', 'application/json')
|
|
75
|
+
|
|
76
|
+
if 'text/html' in accept_header:
|
|
77
|
+
return 'html'
|
|
78
|
+
elif 'text/markdown' in accept_header:
|
|
79
|
+
return 'markdown'
|
|
80
|
+
elif 'text/plain' in accept_header:
|
|
81
|
+
return 'text'
|
|
82
|
+
else:
|
|
83
|
+
return 'json'
|
|
84
|
+
|
|
85
|
+
def _get_output_mode(self, request: web.Request) -> OutputMode:
|
|
86
|
+
"""
|
|
87
|
+
Determine output mode from request headers and parameters.
|
|
88
|
+
|
|
89
|
+
Priority:
|
|
90
|
+
1. Query parameter 'output_mode'
|
|
91
|
+
2. Content-Type header
|
|
92
|
+
3. Accept header
|
|
93
|
+
4. Default to OutputMode.DEFAULT
|
|
94
|
+
"""
|
|
95
|
+
# Check query parameters first
|
|
96
|
+
qs = self.query_parameters(request)
|
|
97
|
+
if 'output_mode' in qs:
|
|
98
|
+
mode = qs['output_mode'].lower()
|
|
99
|
+
if mode in ['json', 'html', 'terminal', 'markdown', 'default']:
|
|
100
|
+
return OutputMode(mode if mode != 'markdown' else 'default')
|
|
101
|
+
|
|
102
|
+
# Check Content-Type header
|
|
103
|
+
content_type = request.headers.get('Content-Type', '').lower()
|
|
104
|
+
if 'application/json' in content_type:
|
|
105
|
+
return OutputMode.JSON
|
|
106
|
+
elif 'text/html' in content_type:
|
|
107
|
+
return OutputMode.HTML
|
|
108
|
+
|
|
109
|
+
# Check Accept header
|
|
110
|
+
accept = request.headers.get('Accept', '').lower()
|
|
111
|
+
if 'application/json' in accept:
|
|
112
|
+
return OutputMode.JSON
|
|
113
|
+
elif 'text/html' in accept:
|
|
114
|
+
return OutputMode.HTML
|
|
115
|
+
elif 'text/plain' in accept:
|
|
116
|
+
return OutputMode.DEFAULT
|
|
117
|
+
|
|
118
|
+
return OutputMode.DEFAULT
|
|
119
|
+
|
|
120
|
+
def _format_to_output_mode(self, format_str: str) -> OutputMode:
|
|
121
|
+
"""
|
|
122
|
+
Convert format string to OutputMode enum.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
format_str: Format string (json, html, markdown, text, terminal)
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
OutputMode enum value
|
|
129
|
+
"""
|
|
130
|
+
format_map = {
|
|
131
|
+
'json': OutputMode.JSON,
|
|
132
|
+
'html': OutputMode.HTML,
|
|
133
|
+
'markdown': OutputMode.DEFAULT,
|
|
134
|
+
'text': OutputMode.DEFAULT,
|
|
135
|
+
'terminal': OutputMode.TERMINAL,
|
|
136
|
+
'default': OutputMode.DEFAULT
|
|
137
|
+
}
|
|
138
|
+
return format_map.get(format_str.lower(), OutputMode.DEFAULT)
|
|
139
|
+
|
|
140
|
+
def _prepare_response(
|
|
141
|
+
self,
|
|
142
|
+
ai_message: AIMessage,
|
|
143
|
+
output_mode: OutputMode,
|
|
144
|
+
format_kwargs: Dict[str, Any] = None
|
|
145
|
+
):
|
|
146
|
+
"""
|
|
147
|
+
Format and return the response based on output mode.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
ai_message: The AIMessage response from the agent
|
|
151
|
+
output_mode: The desired output format
|
|
152
|
+
format_kwargs: Additional formatting options
|
|
153
|
+
"""
|
|
154
|
+
formatter = OutputFormatter()
|
|
155
|
+
|
|
156
|
+
if output_mode == OutputMode.JSON:
|
|
157
|
+
# Return structured JSON response
|
|
158
|
+
response_data = {
|
|
159
|
+
"content": ai_message.content,
|
|
160
|
+
"metadata": {
|
|
161
|
+
"session_id": getattr(ai_message, 'session_id', None),
|
|
162
|
+
"user_id": getattr(ai_message, 'user_id', None),
|
|
163
|
+
"timestamp": getattr(ai_message, 'timestamp', None),
|
|
164
|
+
},
|
|
165
|
+
"tool_calls": getattr(ai_message, 'tool_calls', []),
|
|
166
|
+
"sources": getattr(ai_message, 'documents', []) if hasattr(ai_message, 'documents') else []
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if hasattr(ai_message, 'error') and ai_message.error:
|
|
170
|
+
response_data['error'] = ai_message.error
|
|
171
|
+
return self.json_response(response_data, status=400)
|
|
172
|
+
|
|
173
|
+
return self.json_response(response_data)
|
|
174
|
+
|
|
175
|
+
elif output_mode == OutputMode.HTML:
|
|
176
|
+
# Return formatted HTML
|
|
177
|
+
formatted_content = formatter.format(
|
|
178
|
+
mode=output_mode,
|
|
179
|
+
data=ai_message,
|
|
180
|
+
**(format_kwargs or {})
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Create complete HTML page
|
|
184
|
+
html_template = f"""
|
|
185
|
+
<!DOCTYPE html>
|
|
186
|
+
<html>
|
|
187
|
+
<head>
|
|
188
|
+
<meta charset="UTF-8">
|
|
189
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
190
|
+
<title>Agent Response</title>
|
|
191
|
+
<style>
|
|
192
|
+
body {{
|
|
193
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
194
|
+
max-width: 900px;
|
|
195
|
+
margin: 40px auto;
|
|
196
|
+
padding: 20px;
|
|
197
|
+
line-height: 1.6;
|
|
198
|
+
background-color: #f5f5f5;
|
|
199
|
+
}}
|
|
200
|
+
.response-container {{
|
|
201
|
+
background: white;
|
|
202
|
+
padding: 30px;
|
|
203
|
+
border-radius: 8px;
|
|
204
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
205
|
+
}}
|
|
206
|
+
.metadata {{
|
|
207
|
+
font-size: 0.9em;
|
|
208
|
+
color: #666;
|
|
209
|
+
margin-bottom: 20px;
|
|
210
|
+
padding-bottom: 15px;
|
|
211
|
+
border-bottom: 1px solid #eee;
|
|
212
|
+
}}
|
|
213
|
+
.content {{
|
|
214
|
+
color: #333;
|
|
215
|
+
}}
|
|
216
|
+
.sources {{
|
|
217
|
+
margin-top: 30px;
|
|
218
|
+
padding-top: 20px;
|
|
219
|
+
border-top: 1px solid #eee;
|
|
220
|
+
font-size: 0.9em;
|
|
221
|
+
}}
|
|
222
|
+
code {{
|
|
223
|
+
background: #f4f4f4;
|
|
224
|
+
padding: 2px 6px;
|
|
225
|
+
border-radius: 3px;
|
|
226
|
+
font-family: 'Courier New', monospace;
|
|
227
|
+
}}
|
|
228
|
+
pre {{
|
|
229
|
+
background: #f4f4f4;
|
|
230
|
+
padding: 15px;
|
|
231
|
+
border-radius: 5px;
|
|
232
|
+
overflow-x: auto;
|
|
233
|
+
}}
|
|
234
|
+
</style>
|
|
235
|
+
</head>
|
|
236
|
+
<body>
|
|
237
|
+
<div class="response-container">
|
|
238
|
+
<div class="metadata">
|
|
239
|
+
<strong>Agent Response</strong>
|
|
240
|
+
</div>
|
|
241
|
+
<div class="content">
|
|
242
|
+
{formatted_content}
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
</body>
|
|
246
|
+
</html>
|
|
247
|
+
"""
|
|
248
|
+
return web.Response(
|
|
249
|
+
text=html_template,
|
|
250
|
+
content_type='text/html',
|
|
251
|
+
charset='utf-8'
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
else:
|
|
255
|
+
# Return markdown/plain text
|
|
256
|
+
formatted_content = formatter.format(ai_message, **(format_kwargs or {}))
|
|
257
|
+
return web.Response(
|
|
258
|
+
text=str(formatted_content),
|
|
259
|
+
content_type='text/plain',
|
|
260
|
+
charset='utf-8'
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
async def _add_mcp_servers(self, agent: AbstractBot, mcp_configs: list):
|
|
264
|
+
"""
|
|
265
|
+
Add MCP servers to the agent if it supports MCP.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
agent: The agent instance
|
|
269
|
+
mcp_configs: List of MCP server configurations
|
|
270
|
+
"""
|
|
271
|
+
if not hasattr(agent, 'add_mcp_server'):
|
|
272
|
+
self.logger.warning(
|
|
273
|
+
f"Agent {agent.name} does not support MCP servers. "
|
|
274
|
+
"Ensure BasicAgent has MCPEnabledMixin."
|
|
275
|
+
)
|
|
276
|
+
return
|
|
277
|
+
|
|
278
|
+
for config_dict in mcp_configs:
|
|
279
|
+
try:
|
|
280
|
+
# Create MCPServerConfig from dict
|
|
281
|
+
config = MCPServerConfig(
|
|
282
|
+
name=config_dict.get('name'),
|
|
283
|
+
url=config_dict.get('url'),
|
|
284
|
+
auth_type=config_dict.get('auth_type'),
|
|
285
|
+
auth_config=config_dict.get('auth_config', {}),
|
|
286
|
+
headers=config_dict.get('headers', {}),
|
|
287
|
+
allowed_tools=config_dict.get('allowed_tools'),
|
|
288
|
+
blocked_tools=config_dict.get('blocked_tools'),
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
tools = await agent.add_mcp_server(config)
|
|
292
|
+
self.logger.info(
|
|
293
|
+
f"Added MCP server '{config.name}' with {len(tools)} tools to agent {agent.name}"
|
|
294
|
+
)
|
|
295
|
+
except Exception as e:
|
|
296
|
+
self.logger.error(f"Failed to add MCP server: {e}")
|
|
297
|
+
|
|
298
|
+
def _check_methods(self, bot: AbstractBot, method_name: str):
|
|
299
|
+
"""Check if the method exists in the bot and is callable."""
|
|
300
|
+
forbidden_methods = {
|
|
301
|
+
'__init__', '__del__', '__getattribute__', '__setattr__',
|
|
302
|
+
'configure', '_setup_database_tools', 'save', 'delete',
|
|
303
|
+
'update', 'insert', '__dict__', '__class__', 'retrieval',
|
|
304
|
+
'_define_prompt', 'configure_llm', 'configure_store', 'default_tools'
|
|
305
|
+
}
|
|
306
|
+
if not method_name:
|
|
307
|
+
return None
|
|
308
|
+
if method_name.startswith('_') or method_name in forbidden_methods:
|
|
309
|
+
raise AttributeError(
|
|
310
|
+
f"Method {method_name} error, not found or forbidden."
|
|
311
|
+
)
|
|
312
|
+
if not hasattr(bot, method_name):
|
|
313
|
+
raise AttributeError(
|
|
314
|
+
f"Method {method_name} error, not found or forbidden."
|
|
315
|
+
)
|
|
316
|
+
method = getattr(bot, method_name)
|
|
317
|
+
if not callable(method):
|
|
318
|
+
raise TypeError(
|
|
319
|
+
f"Attribute {method_name} is not callable in bot {bot.name}."
|
|
320
|
+
)
|
|
321
|
+
return method
|
|
322
|
+
|
|
323
|
+
async def _execute_agent_method(
|
|
324
|
+
self,
|
|
325
|
+
bot: AbstractBot,
|
|
326
|
+
method_name: str,
|
|
327
|
+
data: Dict[str, Any],
|
|
328
|
+
attachments: Dict[str, Any],
|
|
329
|
+
use_background: bool,
|
|
330
|
+
) -> web.Response:
|
|
331
|
+
"""Resolve and invoke an agent method safely."""
|
|
332
|
+
try:
|
|
333
|
+
method = self._check_methods(bot, method_name)
|
|
334
|
+
except (AttributeError, TypeError) as exc:
|
|
335
|
+
self.logger.error(f"Method {method_name} not available: {exc}")
|
|
336
|
+
return self.json_response(
|
|
337
|
+
{"error": f"Method {method_name} not available."},
|
|
338
|
+
status=400,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
sig = inspect.signature(method)
|
|
342
|
+
method_params: Dict[str, Any] = {}
|
|
343
|
+
missing_required: List[str] = []
|
|
344
|
+
remaining_kwargs = dict(data)
|
|
345
|
+
|
|
346
|
+
for param_name, param in sig.parameters.items():
|
|
347
|
+
if param_name == 'self' or param_name == 'kwargs':
|
|
348
|
+
continue
|
|
349
|
+
|
|
350
|
+
if param.kind == inspect.Parameter.VAR_POSITIONAL:
|
|
351
|
+
continue
|
|
352
|
+
if param.kind == inspect.Parameter.VAR_KEYWORD:
|
|
353
|
+
continue
|
|
354
|
+
|
|
355
|
+
if param_name in remaining_kwargs:
|
|
356
|
+
method_params[param_name] = remaining_kwargs.pop(param_name)
|
|
357
|
+
elif param.default == inspect.Parameter.empty:
|
|
358
|
+
missing_required.append(param_name)
|
|
359
|
+
|
|
360
|
+
if param_name in attachments:
|
|
361
|
+
method_params[param_name] = attachments[param_name]
|
|
362
|
+
remaining_kwargs.pop(param_name, None)
|
|
363
|
+
|
|
364
|
+
if missing_required:
|
|
365
|
+
return self.json_response(
|
|
366
|
+
{
|
|
367
|
+
"message": (
|
|
368
|
+
"Required parameters missing: "
|
|
369
|
+
f"{', '.join(missing_required)}"
|
|
370
|
+
),
|
|
371
|
+
"required_params": [
|
|
372
|
+
p for p in sig.parameters.keys() if p != 'self'
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
status=400,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
final_kwargs = {**method_params, **remaining_kwargs}
|
|
379
|
+
|
|
380
|
+
try:
|
|
381
|
+
if use_background:
|
|
382
|
+
self.request.app.loop.create_task(method(**final_kwargs))
|
|
383
|
+
return self.json_response(
|
|
384
|
+
{"message": "Request is being processed in the background."}
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
response = await method(**final_kwargs)
|
|
388
|
+
if isinstance(response, web.Response):
|
|
389
|
+
return response
|
|
390
|
+
|
|
391
|
+
return self.json_response(
|
|
392
|
+
{
|
|
393
|
+
"message": (
|
|
394
|
+
f"Method {method_name} was executed successfully."
|
|
395
|
+
),
|
|
396
|
+
"response": str(response),
|
|
397
|
+
}
|
|
398
|
+
)
|
|
399
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
400
|
+
self.logger.error(
|
|
401
|
+
f"Error calling method {method_name}: {exc}",
|
|
402
|
+
exc_info=True,
|
|
403
|
+
)
|
|
404
|
+
return self.json_response(
|
|
405
|
+
{"error": f"Error calling method {method_name}: {exc}"},
|
|
406
|
+
status=500,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
async def post(self):
|
|
410
|
+
"""
|
|
411
|
+
POST handler for agent interaction.
|
|
412
|
+
|
|
413
|
+
Endpoint: POST /api/v1/agents/chat/
|
|
414
|
+
|
|
415
|
+
Request body:
|
|
416
|
+
{
|
|
417
|
+
"agent_name": "my_agent",
|
|
418
|
+
"query": "What is the weather like?",
|
|
419
|
+
"session_id": "optional-session-id",
|
|
420
|
+
"user_id": "optional-user-id",
|
|
421
|
+
"output_mode": "json|html|markdown|terminal|default",
|
|
422
|
+
"search_type": str, # Optional: "similarity", "mmr", "ensemble"
|
|
423
|
+
"use_vector_context": bool, # Optional: Use vector store context
|
|
424
|
+
"format_kwargs": {
|
|
425
|
+
"show_metadata": true,
|
|
426
|
+
"show_sources": true
|
|
427
|
+
},
|
|
428
|
+
"mcp_servers": [
|
|
429
|
+
{
|
|
430
|
+
"name": "weather_api",
|
|
431
|
+
"url": "https://api.example.com/mcp",
|
|
432
|
+
"auth_type": "api_key",
|
|
433
|
+
"auth_config": {"api_key": "xxx"},
|
|
434
|
+
"headers": {"User-Agent": "AI-Parrot/1.0"}
|
|
435
|
+
}
|
|
436
|
+
]
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
- JSON response if output_mode is 'json' or Accept header is application/json
|
|
441
|
+
- HTML page if output_mode is 'html' or Accept header is text/html
|
|
442
|
+
- Markdown/plain text otherwise
|
|
443
|
+
"""
|
|
444
|
+
try:
|
|
445
|
+
qs = self.query_parameters(self.request)
|
|
446
|
+
app = self.request.app
|
|
447
|
+
method_name = self.request.match_info.get('method_name', None)
|
|
448
|
+
try:
|
|
449
|
+
attachments, data = await self.handle_upload()
|
|
450
|
+
except web.HTTPUnsupportedMediaType:
|
|
451
|
+
# if no file is provided, then is a JSON request:
|
|
452
|
+
data = await self.request.json()
|
|
453
|
+
attachments = {}
|
|
454
|
+
|
|
455
|
+
# Support method invocation via body or query parameter in addition to the
|
|
456
|
+
# /{agent_id}/{method_name} route so clients don't need to construct a
|
|
457
|
+
# different URL for maintenance operations like refresh_data.
|
|
458
|
+
method_name = (
|
|
459
|
+
method_name
|
|
460
|
+
or data.pop('method_name', None)
|
|
461
|
+
or qs.get('method_name')
|
|
462
|
+
)
|
|
463
|
+
# Get BotManager
|
|
464
|
+
manager = self.request.app.get('bot_manager')
|
|
465
|
+
if not manager:
|
|
466
|
+
return self.json_response(
|
|
467
|
+
{"error": "BotManager is not installed."},
|
|
468
|
+
status=500
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
# Extract agent name
|
|
472
|
+
agent_name = self.request.match_info.get('agent_id', None)
|
|
473
|
+
if not agent_name:
|
|
474
|
+
agent_name = data.pop('agent_name', None) or qs.get('agent_name')
|
|
475
|
+
if not agent_name:
|
|
476
|
+
return self.error(
|
|
477
|
+
"Missing Agent Name",
|
|
478
|
+
status=400
|
|
479
|
+
)
|
|
480
|
+
query = data.pop('query', None)
|
|
481
|
+
# Get the agent
|
|
482
|
+
try:
|
|
483
|
+
agent: AbstractBot = await manager.get_bot(agent_name)
|
|
484
|
+
if not agent:
|
|
485
|
+
return self.error(
|
|
486
|
+
f"Agent '{agent_name}' not found.",
|
|
487
|
+
status=404
|
|
488
|
+
)
|
|
489
|
+
except Exception as e:
|
|
490
|
+
self.logger.error(f"Error retrieving agent {agent_name}: {e}")
|
|
491
|
+
return self.error(
|
|
492
|
+
f"Error retrieving agent: {e}",
|
|
493
|
+
status=500
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
# task background:
|
|
497
|
+
use_background = data.pop('background', False)
|
|
498
|
+
|
|
499
|
+
# Add MCP servers if provided
|
|
500
|
+
mcp_servers = data.pop('mcp_servers', [])
|
|
501
|
+
if mcp_servers and isinstance(mcp_servers, list):
|
|
502
|
+
await self._add_mcp_servers(agent, mcp_servers)
|
|
503
|
+
|
|
504
|
+
# TODO: Get session information
|
|
505
|
+
session_id = data.pop('session_id', None)
|
|
506
|
+
user_id = data.pop('user_id', None)
|
|
507
|
+
|
|
508
|
+
# Try to get from request session if not provided
|
|
509
|
+
try:
|
|
510
|
+
request_session = self.request.session
|
|
511
|
+
if not session_id:
|
|
512
|
+
session_id = request_session.get('session_id')
|
|
513
|
+
if not user_id:
|
|
514
|
+
user_id = request_session.get('user_id')
|
|
515
|
+
except AttributeError:
|
|
516
|
+
pass
|
|
517
|
+
|
|
518
|
+
# Determine output mode
|
|
519
|
+
# output_mode = self._get_output_mode(self.request)
|
|
520
|
+
# Determine output format
|
|
521
|
+
output_format = self._get_output_format(data, qs)
|
|
522
|
+
output_mode = data.pop('output_mode', OutputMode.DEFAULT)
|
|
523
|
+
|
|
524
|
+
# Extract parameters for ask()
|
|
525
|
+
search_type = data.pop('search_type', 'similarity')
|
|
526
|
+
return_sources = data.pop('return_sources', True)
|
|
527
|
+
use_vector_context = data.pop('use_vector_context', True)
|
|
528
|
+
use_conversation_history = data.pop('use_conversation_history', True)
|
|
529
|
+
followup_turn_id = data.pop('turn_id', None)
|
|
530
|
+
followup_data = data.pop('data', None)
|
|
531
|
+
|
|
532
|
+
# Override with explicit parameter if provided
|
|
533
|
+
if 'output_mode' in data:
|
|
534
|
+
try:
|
|
535
|
+
output_mode = OutputMode(data.pop('output_mode'))
|
|
536
|
+
except ValueError:
|
|
537
|
+
pass
|
|
538
|
+
|
|
539
|
+
# Prepare ask() parameters
|
|
540
|
+
format_kwargs = data.pop('format_kwargs', {})
|
|
541
|
+
response = None
|
|
542
|
+
async with agent.retrieval(self.request, app=app) as bot:
|
|
543
|
+
if method_name:
|
|
544
|
+
return await self._execute_agent_method(
|
|
545
|
+
bot=bot,
|
|
546
|
+
method_name=method_name,
|
|
547
|
+
data=data,
|
|
548
|
+
attachments=attachments,
|
|
549
|
+
use_background=use_background,
|
|
550
|
+
)
|
|
551
|
+
else:
|
|
552
|
+
if not query:
|
|
553
|
+
return self.json_response(
|
|
554
|
+
{"error": "query is required"},
|
|
555
|
+
status=400
|
|
556
|
+
)
|
|
557
|
+
if isinstance(bot, PandasAgent) and followup_turn_id and followup_data is not None:
|
|
558
|
+
response: AIMessage = await bot.followup(
|
|
559
|
+
question=query,
|
|
560
|
+
turn_id=followup_turn_id,
|
|
561
|
+
data=followup_data,
|
|
562
|
+
session_id=session_id,
|
|
563
|
+
user_id=user_id,
|
|
564
|
+
use_conversation_history=use_conversation_history,
|
|
565
|
+
output_mode=output_mode,
|
|
566
|
+
format_kwargs=format_kwargs,
|
|
567
|
+
**data,
|
|
568
|
+
)
|
|
569
|
+
else:
|
|
570
|
+
response: AIMessage = await bot.ask(
|
|
571
|
+
question=query,
|
|
572
|
+
session_id=session_id,
|
|
573
|
+
user_id=user_id,
|
|
574
|
+
search_type=search_type,
|
|
575
|
+
return_sources=return_sources,
|
|
576
|
+
use_vector_context=use_vector_context,
|
|
577
|
+
use_conversation_history=use_conversation_history,
|
|
578
|
+
output_mode=output_mode,
|
|
579
|
+
format_kwargs=format_kwargs,
|
|
580
|
+
**data,
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
# Return formatted response
|
|
584
|
+
return self._format_response(
|
|
585
|
+
response,
|
|
586
|
+
output_format,
|
|
587
|
+
format_kwargs
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
except json.JSONDecodeError:
|
|
591
|
+
return self.json_response(
|
|
592
|
+
{"error": "Invalid JSON in request body"},
|
|
593
|
+
status=400
|
|
594
|
+
)
|
|
595
|
+
except Exception as e:
|
|
596
|
+
self.logger.error(
|
|
597
|
+
f"Error in AgentTalk: {e}", exc_info=True
|
|
598
|
+
)
|
|
599
|
+
return self.json_response(
|
|
600
|
+
{
|
|
601
|
+
"error": "Internal server error",
|
|
602
|
+
"message": str(e)
|
|
603
|
+
},
|
|
604
|
+
status=500
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
async def get(self):
|
|
608
|
+
"""
|
|
609
|
+
GET /api/v1/agents/chat/
|
|
610
|
+
|
|
611
|
+
Returns information about the AgentTalk endpoint.
|
|
612
|
+
"""
|
|
613
|
+
method_name = self.request.match_info.get('method_name', None)
|
|
614
|
+
if method_name == 'debug':
|
|
615
|
+
agent_name = self.request.match_info.get('agent_id', None)
|
|
616
|
+
if not agent_name:
|
|
617
|
+
return self.error(
|
|
618
|
+
"Missing Agent Name for debug.",
|
|
619
|
+
status=400
|
|
620
|
+
)
|
|
621
|
+
manager = self.request.app.get('bot_manager')
|
|
622
|
+
if not manager:
|
|
623
|
+
return self.json_response(
|
|
624
|
+
{"error": "BotManager is not installed."},
|
|
625
|
+
status=500
|
|
626
|
+
)
|
|
627
|
+
try:
|
|
628
|
+
agent: AbstractBot = await manager.get_bot(agent_name)
|
|
629
|
+
if not agent:
|
|
630
|
+
return self.error(
|
|
631
|
+
f"Agent '{agent_name}' not found.",
|
|
632
|
+
status=404
|
|
633
|
+
)
|
|
634
|
+
except Exception as e:
|
|
635
|
+
self.logger.error(f"Error retrieving agent {agent_name}: {e}")
|
|
636
|
+
return self.error(
|
|
637
|
+
f"Error retrieving agent: {e}",
|
|
638
|
+
status=500
|
|
639
|
+
)
|
|
640
|
+
debug_info = await self.debug_agent(agent)
|
|
641
|
+
return self.json_response(debug_info)
|
|
642
|
+
|
|
643
|
+
return self.json_response({
|
|
644
|
+
"message": "AgentTalk - Universal Agent Conversation Interface",
|
|
645
|
+
"version": "1.0",
|
|
646
|
+
"usage": {
|
|
647
|
+
"method": "POST",
|
|
648
|
+
"endpoint": "/api/v1/agents/chat/",
|
|
649
|
+
"required_fields": ["agent_name", "query"],
|
|
650
|
+
"optional_fields": [
|
|
651
|
+
"session_id",
|
|
652
|
+
"user_id",
|
|
653
|
+
"output_mode",
|
|
654
|
+
"format_kwargs",
|
|
655
|
+
"mcp_servers",
|
|
656
|
+
"ask_kwargs"
|
|
657
|
+
],
|
|
658
|
+
"output_modes": ["json", "html", "markdown", "terminal", "default"]
|
|
659
|
+
}
|
|
660
|
+
})
|
|
661
|
+
|
|
662
|
+
def _format_response(
|
|
663
|
+
self,
|
|
664
|
+
response: Union[AIMessage, AgentResponse],
|
|
665
|
+
output_format: str,
|
|
666
|
+
format_kwargs: Dict[str, Any]
|
|
667
|
+
) -> web.Response:
|
|
668
|
+
"""
|
|
669
|
+
Format the response based on the requested output format.
|
|
670
|
+
|
|
671
|
+
Args:
|
|
672
|
+
response: AIMessage from agent
|
|
673
|
+
output_format: Requested format
|
|
674
|
+
format_kwargs: Additional formatting options
|
|
675
|
+
|
|
676
|
+
Returns:
|
|
677
|
+
web.Response with appropriate content type
|
|
678
|
+
"""
|
|
679
|
+
|
|
680
|
+
if isinstance(response, AgentResponse):
|
|
681
|
+
response = response.response
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
output = response.output
|
|
685
|
+
|
|
686
|
+
if output_format == 'json':
|
|
687
|
+
# Return structured JSON response
|
|
688
|
+
if isinstance(output, pd.DataFrame):
|
|
689
|
+
# Convert DataFrame to dict
|
|
690
|
+
output = output.to_dict(orient='records')
|
|
691
|
+
elif hasattr(output, 'explanation'):
|
|
692
|
+
output = output.explanation
|
|
693
|
+
output_mode = response.output_mode or 'json'
|
|
694
|
+
obj_response = {
|
|
695
|
+
"input": response.input,
|
|
696
|
+
"output": output,
|
|
697
|
+
"data": response.data,
|
|
698
|
+
"response": response.response,
|
|
699
|
+
"output_mode": output_mode,
|
|
700
|
+
"code": str(response.code) if response.code else None,
|
|
701
|
+
"metadata": {
|
|
702
|
+
"model": getattr(response, 'model', None),
|
|
703
|
+
"provider": getattr(response, 'provider', None),
|
|
704
|
+
"session_id": str(getattr(response, 'session_id', '')),
|
|
705
|
+
"turn_id": str(getattr(response, 'turn_id', '')),
|
|
706
|
+
"response_time": getattr(response, 'response_time', None),
|
|
707
|
+
},
|
|
708
|
+
"sources": [
|
|
709
|
+
{
|
|
710
|
+
"content": source.content,
|
|
711
|
+
"metadata": source.metadata
|
|
712
|
+
}
|
|
713
|
+
for source in getattr(response, 'sources', [])
|
|
714
|
+
] if format_kwargs.get('include_sources', True) else [],
|
|
715
|
+
"tool_calls": [
|
|
716
|
+
{
|
|
717
|
+
"name": getattr(tool, 'name', 'unknown'),
|
|
718
|
+
"status": getattr(tool, 'status', 'completed'),
|
|
719
|
+
"output": getattr(tool, 'output', None),
|
|
720
|
+
'arguments': getattr(tool, 'arguments', None)
|
|
721
|
+
}
|
|
722
|
+
for tool in getattr(response, 'tool_calls', [])
|
|
723
|
+
] if format_kwargs.get('include_tool_calls', True) else []
|
|
724
|
+
}
|
|
725
|
+
print(obj_response)
|
|
726
|
+
return web.json_response(
|
|
727
|
+
obj_response, dumps=json_encoder
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
elif output_format == 'html':
|
|
731
|
+
interactive = format_kwargs.get('interactive', False)
|
|
732
|
+
if interactive:
|
|
733
|
+
return self._serve_panel_dashboard(response)
|
|
734
|
+
|
|
735
|
+
# Return HTML response
|
|
736
|
+
html_content = response.response
|
|
737
|
+
if isinstance(html_content, str):
|
|
738
|
+
html_str = html_content
|
|
739
|
+
elif hasattr(html_content, '_repr_html_'):
|
|
740
|
+
# Panel/IPython displayable object (for HTML mode)
|
|
741
|
+
html_str = html_content._repr_html_()
|
|
742
|
+
elif hasattr(html_content, '__str__'):
|
|
743
|
+
# Other objects with string representation
|
|
744
|
+
html_str = str(html_content)
|
|
745
|
+
else:
|
|
746
|
+
html_str = str(html_content)
|
|
747
|
+
|
|
748
|
+
return web.Response(
|
|
749
|
+
text=html_str,
|
|
750
|
+
content_type='text/html',
|
|
751
|
+
charset='utf-8'
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
else: # markdown or text
|
|
755
|
+
# Return plain text/markdown response
|
|
756
|
+
content = response.content
|
|
757
|
+
|
|
758
|
+
# Ensure it's a string
|
|
759
|
+
if not isinstance(content, str):
|
|
760
|
+
content = str(content)
|
|
761
|
+
|
|
762
|
+
# Optionally append sources
|
|
763
|
+
if format_kwargs.get('include_sources', False) and hasattr(response, 'sources'):
|
|
764
|
+
content += "\n\n## Sources\n"
|
|
765
|
+
for idx, source in enumerate(response.sources, 1):
|
|
766
|
+
content += f"\n{idx}. {source.content[:200]}...\n"
|
|
767
|
+
|
|
768
|
+
return web.Response(
|
|
769
|
+
text=content,
|
|
770
|
+
content_type='text/plain' if output_format == 'text' else 'text/markdown',
|
|
771
|
+
charset='utf-8'
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
return html_template
|
|
775
|
+
|
|
776
|
+
def _serve_panel_dashboard(self, response: AIMessage) -> web.Response:
|
|
777
|
+
"""
|
|
778
|
+
Serve an interactive Panel dashboard.
|
|
779
|
+
|
|
780
|
+
This converts the Panel object to a standalone HTML application
|
|
781
|
+
with embedded JavaScript for interactivity.
|
|
782
|
+
|
|
783
|
+
Args:
|
|
784
|
+
response: AIMessage with Panel object in .content
|
|
785
|
+
|
|
786
|
+
Returns:
|
|
787
|
+
web.Response with interactive HTML
|
|
788
|
+
"""
|
|
789
|
+
try:
|
|
790
|
+
panel_obj = response.response
|
|
791
|
+
# Create temporary file for the Panel HTML
|
|
792
|
+
with tempfile.NamedTemporaryFile(
|
|
793
|
+
mode='w',
|
|
794
|
+
suffix='.html',
|
|
795
|
+
delete=False
|
|
796
|
+
) as tmp:
|
|
797
|
+
tmp_path = tmp.name
|
|
798
|
+
|
|
799
|
+
try:
|
|
800
|
+
# Save Panel to HTML with all resources embedded
|
|
801
|
+
panel_obj.save(
|
|
802
|
+
tmp_path,
|
|
803
|
+
embed=True, # Embed all JS/CSS resources
|
|
804
|
+
title=f"AI Agent Response - {response.session_id[:8] if response.session_id else 'interactive'}",
|
|
805
|
+
resources='inline' # Inline all resources
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
# Read the HTML content
|
|
809
|
+
with open(tmp_path, 'r', encoding='utf-8') as f:
|
|
810
|
+
html_content = f.read()
|
|
811
|
+
|
|
812
|
+
# Return as HTML response
|
|
813
|
+
return web.Response(
|
|
814
|
+
text=html_content,
|
|
815
|
+
content_type='text/html',
|
|
816
|
+
charset='utf-8'
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
finally:
|
|
820
|
+
# Clean up temporary file
|
|
821
|
+
if os.path.exists(tmp_path):
|
|
822
|
+
try:
|
|
823
|
+
os.unlink(tmp_path)
|
|
824
|
+
except Exception as e:
|
|
825
|
+
self.logger.warning(f"Failed to delete temp file {tmp_path}: {e}")
|
|
826
|
+
|
|
827
|
+
except ImportError:
|
|
828
|
+
self.logger.error(
|
|
829
|
+
"Panel library not available for interactive dashboards"
|
|
830
|
+
)
|
|
831
|
+
# Fallback to static HTML
|
|
832
|
+
return web.Response(
|
|
833
|
+
text=str(response.content),
|
|
834
|
+
content_type='text/html',
|
|
835
|
+
charset='utf-8'
|
|
836
|
+
)
|
|
837
|
+
except Exception as e:
|
|
838
|
+
self.logger.error(f"Error serving Panel dashboard: {e}", exc_info=True)
|
|
839
|
+
# Fallback to error response
|
|
840
|
+
return self.error(
|
|
841
|
+
f"Error rendering interactive dashboard: {e}",
|
|
842
|
+
status=500
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
async def debug_agent(self, agent):
|
|
846
|
+
return {
|
|
847
|
+
"dataframes": list(agent.dataframes.keys()),
|
|
848
|
+
"df_metadata": {k: v['shape'] for k, v in agent.df_metadata.items()},
|
|
849
|
+
"pandas_tool": {
|
|
850
|
+
"exists": agent._get_python_pandas_tool() is not None,
|
|
851
|
+
"dataframes": list(agent._get_python_pandas_tool().dataframes.keys())
|
|
852
|
+
if agent._get_python_pandas_tool() else []
|
|
853
|
+
},
|
|
854
|
+
"metadata_tool": {
|
|
855
|
+
"exists": agent._get_metadata_tool() is not None,
|
|
856
|
+
"dataframes": list(agent._get_metadata_tool().dataframes.keys())
|
|
857
|
+
if agent._get_metadata_tool() else [],
|
|
858
|
+
"metadata": list(agent._get_metadata_tool().metadata.keys())
|
|
859
|
+
if agent._get_metadata_tool() else []
|
|
860
|
+
}
|
|
861
|
+
}
|