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/tools/abstract.py
ADDED
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Abstract Tool base class for all function-calling tools.in ai-parrot framework.
|
|
3
|
+
"""
|
|
4
|
+
import importlib
|
|
5
|
+
import inspect
|
|
6
|
+
import os
|
|
7
|
+
from typing import Dict, Any, Union, Optional, Type
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
import traceback
|
|
12
|
+
import logging
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from urllib.parse import urlparse, urlunparse
|
|
15
|
+
from pydantic import BaseModel, Field
|
|
16
|
+
from datamodel.parsers.json import json_decoder, json_encoder, JSONContent # noqa pylint: disable=E0611
|
|
17
|
+
from navconfig.logging import logging
|
|
18
|
+
from ..conf import BASE_STATIC_URL, STATIC_DIR
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
logging.getLogger(name='matplotlib').setLevel(logging.INFO)
|
|
22
|
+
logging.getLogger(name='h5py').setLevel(logging.INFO)
|
|
23
|
+
logging.getLogger(name='datasets').setLevel(logging.WARNING)
|
|
24
|
+
logging.getLogger(name='numexpr').setLevel(logging.WARNING)
|
|
25
|
+
logging.getLogger(name='pymongo').setLevel(logging.WARNING)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AbstractToolArgsSchema(BaseModel):
|
|
29
|
+
"""Base schema for tool arguments."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ToolResult(BaseModel):
|
|
34
|
+
"""Standardized tool result format."""
|
|
35
|
+
success: bool = Field(default=True, description="Indicates if the tool executed successfully")
|
|
36
|
+
status: str = Field(default="success", description="Status of the operation")
|
|
37
|
+
result: Any = Field(description="The actual result of the tool operation")
|
|
38
|
+
error: Optional[str] = Field(default=None, description="Error message if any")
|
|
39
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
40
|
+
timestamp: str = Field(default_factory=lambda: datetime.now().isoformat())
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AbstractTool(ABC):
|
|
44
|
+
"""
|
|
45
|
+
Abstract base class for all tools in the ai-parrot framework.
|
|
46
|
+
|
|
47
|
+
This class provides a unified interface for tools that can be used by both
|
|
48
|
+
conversational bots and agents. It includes common functionality like:
|
|
49
|
+
- Name and description management
|
|
50
|
+
- JSON schema generation
|
|
51
|
+
- File path management
|
|
52
|
+
- Logging and error handling
|
|
53
|
+
- Async/sync execution support
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# Class attributes that should be set by subclasses
|
|
57
|
+
name: str = None
|
|
58
|
+
description: str = None
|
|
59
|
+
args_schema: Type[BaseModel] = AbstractToolArgsSchema
|
|
60
|
+
return_direct: bool = False
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
name: Optional[str] = None,
|
|
65
|
+
description: Optional[str] = None,
|
|
66
|
+
output_dir: Optional[Union[str, Path]] = None,
|
|
67
|
+
base_url: Optional[str] = None,
|
|
68
|
+
static_dir: Optional[Union[str, Path]] = None,
|
|
69
|
+
**kwargs
|
|
70
|
+
):
|
|
71
|
+
"""
|
|
72
|
+
Initialize the tool.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
name: Tool name (defaults to class name)
|
|
76
|
+
description: Tool description
|
|
77
|
+
output_dir: Directory for output files (if tool generates files)
|
|
78
|
+
base_url: Base URL for serving static files
|
|
79
|
+
static_dir: Static directory path
|
|
80
|
+
**kwargs: Additional configuration
|
|
81
|
+
"""
|
|
82
|
+
# Store initialization parameters for cloning
|
|
83
|
+
self._init_kwargs = {
|
|
84
|
+
'name': name,
|
|
85
|
+
'description': description,
|
|
86
|
+
'output_dir': output_dir,
|
|
87
|
+
'base_url': base_url,
|
|
88
|
+
'static_dir': static_dir,
|
|
89
|
+
**kwargs
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Set name and description
|
|
93
|
+
self.name = name or self.name or self.__class__.__name__
|
|
94
|
+
self.description = description or self.__class__.__doc__ or f"Tool: {self.name}"
|
|
95
|
+
|
|
96
|
+
# Set up logging
|
|
97
|
+
self.logger = logging.getLogger(
|
|
98
|
+
f'{self.name}.Tool'
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# JSON encoders/decoders
|
|
102
|
+
self._json_encoder = json_encoder
|
|
103
|
+
self._json_decoder = json_decoder
|
|
104
|
+
self._json = JSONContent()
|
|
105
|
+
|
|
106
|
+
# File and URL configuration
|
|
107
|
+
self.base_url = base_url or BASE_STATIC_URL
|
|
108
|
+
self.static_url = base_url or BASE_STATIC_URL
|
|
109
|
+
parsed = urlparse(self.static_url)
|
|
110
|
+
self._base_scheme_netloc = (parsed.scheme, parsed.netloc)
|
|
111
|
+
|
|
112
|
+
# Set up directories
|
|
113
|
+
self.static_dir = Path(static_dir or STATIC_DIR).resolve()
|
|
114
|
+
|
|
115
|
+
self.output_dir = Path(output_dir).resolve() if output_dir else self._default_output_dir()
|
|
116
|
+
|
|
117
|
+
# Ensure output directory exists if specified
|
|
118
|
+
if self.output_dir and not self.output_dir.exists():
|
|
119
|
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
120
|
+
|
|
121
|
+
def _default_output_dir(self) -> Optional[Path]:
|
|
122
|
+
"""Get the default output directory for this tool type."""
|
|
123
|
+
# Default implementation - tools that don't need output can return None
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
def _get_clone_kwargs(self) -> Dict[str, Any]:
|
|
127
|
+
"""
|
|
128
|
+
Get the keyword arguments to use when cloning this tool.
|
|
129
|
+
|
|
130
|
+
Subclasses can override this method to customize which parameters
|
|
131
|
+
are cloned and which are not. By default, all initialization
|
|
132
|
+
parameters stored in _init_kwargs are returned.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Dictionary of keyword arguments for tool initialization
|
|
136
|
+
"""
|
|
137
|
+
return self._init_kwargs.copy()
|
|
138
|
+
|
|
139
|
+
def clone(self):
|
|
140
|
+
"""
|
|
141
|
+
Create a new instance of this tool with the same configuration.
|
|
142
|
+
|
|
143
|
+
This method creates a new instance of the tool class with all the
|
|
144
|
+
initialization parameters that were passed to the current instance.
|
|
145
|
+
Subclasses can override _get_clone_kwargs() to customize which
|
|
146
|
+
parameters are cloned and which are not.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
New instance of the same tool class with cloned configuration
|
|
150
|
+
|
|
151
|
+
Example:
|
|
152
|
+
>>> dbtool = DatabaseTool(connection_string="postgresql://...")
|
|
153
|
+
>>> new_tool = dbtool.clone()
|
|
154
|
+
>>> # new_tool is a fresh instance with the same configuration
|
|
155
|
+
"""
|
|
156
|
+
clone_kwargs = self._get_clone_kwargs()
|
|
157
|
+
return self.__class__(**clone_kwargs)
|
|
158
|
+
|
|
159
|
+
@abstractmethod
|
|
160
|
+
async def _execute(self, **kwargs) -> Any:
|
|
161
|
+
"""
|
|
162
|
+
Execute the tool with the given arguments.
|
|
163
|
+
This is the main method that subclasses must implement.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
**kwargs: Tool arguments
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Tool execution result
|
|
170
|
+
"""
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
def get_tool_schema(self) -> Dict[str, Any]:
|
|
174
|
+
"""
|
|
175
|
+
Get the JSON schema for this tool.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
JSON schema dictionary compatible with LLM tool registration
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
def _enforce_no_extra_fields(definition: Any) -> None:
|
|
182
|
+
"""Recursively set ``additionalProperties`` to ``False`` for objects.
|
|
183
|
+
|
|
184
|
+
OpenAI tools require every object schema (including nested ones) to
|
|
185
|
+
explicitly disallow extra properties. Pydantic's generated schema only
|
|
186
|
+
sets this flag at the top level, so we walk the entire schema tree and
|
|
187
|
+
ensure every object definition is strict.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
if not isinstance(definition, dict):
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
if definition.get("type") == "object":
|
|
194
|
+
definition.setdefault("properties", {})
|
|
195
|
+
definition.setdefault("additionalProperties", False)
|
|
196
|
+
|
|
197
|
+
# Recurse into common schema containers
|
|
198
|
+
for key in ("properties", "patternProperties"):
|
|
199
|
+
if isinstance(definition.get(key), dict):
|
|
200
|
+
for sub in definition[key].values():
|
|
201
|
+
_enforce_no_extra_fields(sub)
|
|
202
|
+
|
|
203
|
+
for key in ("items", "additionalItems"):
|
|
204
|
+
_enforce_no_extra_fields(definition.get(key))
|
|
205
|
+
|
|
206
|
+
for key in ("anyOf", "oneOf", "allOf"):
|
|
207
|
+
if isinstance(definition.get(key), list):
|
|
208
|
+
for sub in definition[key]:
|
|
209
|
+
_enforce_no_extra_fields(sub)
|
|
210
|
+
|
|
211
|
+
# Handle $defs/definitions used by Pydantic
|
|
212
|
+
for key in ("$defs", "definitions"):
|
|
213
|
+
if isinstance(definition.get(key), dict):
|
|
214
|
+
for sub in definition[key].values():
|
|
215
|
+
_enforce_no_extra_fields(sub)
|
|
216
|
+
|
|
217
|
+
schema = {
|
|
218
|
+
"name": self.name,
|
|
219
|
+
"description": self.description,
|
|
220
|
+
"parameters": {
|
|
221
|
+
"type": "object",
|
|
222
|
+
"properties": {},
|
|
223
|
+
"required": [],
|
|
224
|
+
"additionalProperties": False
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# If args_schema is defined, use it to build the parameters
|
|
229
|
+
if self.args_schema and self.args_schema != AbstractToolArgsSchema:
|
|
230
|
+
pydantic_schema = self.args_schema.model_json_schema()
|
|
231
|
+
schema["parameters"] = {
|
|
232
|
+
"type": "object",
|
|
233
|
+
"properties": pydantic_schema.get("properties", {}),
|
|
234
|
+
"required": pydantic_schema.get("required", []),
|
|
235
|
+
"additionalProperties": False,
|
|
236
|
+
"$defs": pydantic_schema.get("$defs", {}),
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
_enforce_no_extra_fields(schema["parameters"])
|
|
240
|
+
return schema
|
|
241
|
+
|
|
242
|
+
def validate_args(self, **kwargs) -> BaseModel:
|
|
243
|
+
"""
|
|
244
|
+
Validate arguments using the tool's schema.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
**kwargs: Arguments to validate
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Validated arguments as Pydantic model instance
|
|
251
|
+
"""
|
|
252
|
+
if not self.args_schema or self.args_schema == AbstractToolArgsSchema:
|
|
253
|
+
# If no schema is defined, return a basic model with the kwargs
|
|
254
|
+
return AbstractToolArgsSchema()
|
|
255
|
+
try:
|
|
256
|
+
result = self.args_schema(**kwargs)
|
|
257
|
+
if not result:
|
|
258
|
+
self.logger.warning(
|
|
259
|
+
f"Validation failed for {self.name} with args: {kwargs}"
|
|
260
|
+
)
|
|
261
|
+
return result
|
|
262
|
+
except Exception as e:
|
|
263
|
+
self.logger.error(f"Validation error in {self.name}: {e}")
|
|
264
|
+
raise ValueError(
|
|
265
|
+
f"Invalid arguments for {self.name}: {e}"
|
|
266
|
+
) from e
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
async def execute(self, *args, **kwargs) -> ToolResult:
|
|
270
|
+
"""
|
|
271
|
+
Execute the tool with error handling and result standardization.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
**kwargs: Tool arguments
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Standardized ToolResult
|
|
278
|
+
|
|
279
|
+
TODO: Use the Global Registry to share data between tools.
|
|
280
|
+
"""
|
|
281
|
+
try:
|
|
282
|
+
self.logger.notice(f"Executing tool: {self.name}")
|
|
283
|
+
|
|
284
|
+
# Validate arguments
|
|
285
|
+
validated_args = self.validate_args(**kwargs)
|
|
286
|
+
|
|
287
|
+
# Execute the tool
|
|
288
|
+
if hasattr(validated_args, 'model_dump'):
|
|
289
|
+
result = await self._execute(*args, **validated_args.model_dump())
|
|
290
|
+
else:
|
|
291
|
+
result = await self._execute(*args, **kwargs)
|
|
292
|
+
|
|
293
|
+
# if is an toolResult, return it directly
|
|
294
|
+
if isinstance(result, ToolResult):
|
|
295
|
+
return result
|
|
296
|
+
elif isinstance(result, dict) and 'status' in result and 'result' in result:
|
|
297
|
+
try:
|
|
298
|
+
return ToolResult(**result)
|
|
299
|
+
except Exception as e:
|
|
300
|
+
self.logger.error(f"Error creating ToolResult from dict: {e}")
|
|
301
|
+
return ToolResult(
|
|
302
|
+
status="done_with_errors",
|
|
303
|
+
result=result.get('result', []),
|
|
304
|
+
error=f"Error creating ToolResult: {e}",
|
|
305
|
+
metadata=result.get('metadata', {})
|
|
306
|
+
)
|
|
307
|
+
if result is None:
|
|
308
|
+
raise ValueError(
|
|
309
|
+
"Tool execution returned None, expected a result."
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
self.logger.info(
|
|
313
|
+
f"Tool {self.name} executed successfully"
|
|
314
|
+
)
|
|
315
|
+
# print('TYPE > ', type(result), ' RESULT > ', result)
|
|
316
|
+
|
|
317
|
+
return ToolResult(
|
|
318
|
+
status="success",
|
|
319
|
+
result=result,
|
|
320
|
+
metadata={
|
|
321
|
+
"tool_name": self.name,
|
|
322
|
+
"execution_time": datetime.now().isoformat()
|
|
323
|
+
}
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
except Exception as e:
|
|
327
|
+
print('ERROR')
|
|
328
|
+
print(f'============ {e} ============')
|
|
329
|
+
error_msg = f"Error in {self.name}: {str(e)}"
|
|
330
|
+
self.logger.error(error_msg)
|
|
331
|
+
self.logger.error(traceback.format_exc())
|
|
332
|
+
|
|
333
|
+
return ToolResult(
|
|
334
|
+
status="error",
|
|
335
|
+
result=None,
|
|
336
|
+
error=error_msg,
|
|
337
|
+
metadata={
|
|
338
|
+
"tool_name": self.name,
|
|
339
|
+
"error_type": type(e).__name__
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
run = execute # Alias for compatibility with sync code
|
|
344
|
+
|
|
345
|
+
# Utility methods for file handling (inherited from BaseAbstractTool)
|
|
346
|
+
def to_static_url(self, file_path: Union[str, Path]) -> str:
|
|
347
|
+
"""
|
|
348
|
+
Convert an absolute file path to a static URL.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
file_path: Absolute path to the file
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
URL-based path for serving the static file
|
|
355
|
+
"""
|
|
356
|
+
if not self.static_dir:
|
|
357
|
+
return str(file_path)
|
|
358
|
+
|
|
359
|
+
file_path = Path(file_path)
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
relative_path = file_path.relative_to(self.static_dir)
|
|
363
|
+
return f"{self.static_url.rstrip('/')}/{relative_path}"
|
|
364
|
+
except ValueError:
|
|
365
|
+
self.logger.warning(
|
|
366
|
+
f"File {file_path} is not within static directory {self.static_dir}"
|
|
367
|
+
)
|
|
368
|
+
return str(file_path)
|
|
369
|
+
|
|
370
|
+
def relative_url(self, url: str) -> str:
|
|
371
|
+
"""
|
|
372
|
+
Convert an absolute URL to a relative URL based on the base URL.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
url: Absolute URL to convert
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
Relative URL based on the base URL
|
|
379
|
+
"""
|
|
380
|
+
parts = urlparse(url)
|
|
381
|
+
if not parts.scheme or not parts.netloc:
|
|
382
|
+
return url
|
|
383
|
+
|
|
384
|
+
if (parts.scheme, parts.netloc) == self._base_scheme_netloc:
|
|
385
|
+
return urlunparse((
|
|
386
|
+
"", "", parts.path, parts.params, parts.query, parts.fragment
|
|
387
|
+
))
|
|
388
|
+
return url
|
|
389
|
+
|
|
390
|
+
def generate_filename(
|
|
391
|
+
self,
|
|
392
|
+
prefix: str = "output",
|
|
393
|
+
extension: str = "",
|
|
394
|
+
include_timestamp: bool = True
|
|
395
|
+
) -> str:
|
|
396
|
+
"""
|
|
397
|
+
Generate a unique filename with optional timestamp.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
prefix: File prefix
|
|
401
|
+
extension: File extension (with or without dot)
|
|
402
|
+
include_timestamp: Whether to include timestamp
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
Generated filename
|
|
406
|
+
"""
|
|
407
|
+
if include_timestamp:
|
|
408
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
409
|
+
filename = f"{prefix}_{timestamp}"
|
|
410
|
+
else:
|
|
411
|
+
filename = prefix
|
|
412
|
+
|
|
413
|
+
if extension:
|
|
414
|
+
if not extension.startswith('.'):
|
|
415
|
+
extension = f".{extension}"
|
|
416
|
+
filename += extension
|
|
417
|
+
|
|
418
|
+
return filename
|
|
419
|
+
|
|
420
|
+
def validate_output_path(self, file_path: Union[str, Path]) -> Path:
|
|
421
|
+
"""
|
|
422
|
+
Validate and ensure the output path is within allowed directories.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
file_path: Path to validate
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
Validated Path object
|
|
429
|
+
|
|
430
|
+
Raises:
|
|
431
|
+
ValueError: If path is outside allowed directories
|
|
432
|
+
"""
|
|
433
|
+
if not self.static_dir:
|
|
434
|
+
return Path(file_path).resolve()
|
|
435
|
+
|
|
436
|
+
file_path = Path(file_path).resolve()
|
|
437
|
+
|
|
438
|
+
try:
|
|
439
|
+
file_path.relative_to(self.static_dir.resolve())
|
|
440
|
+
except ValueError as e:
|
|
441
|
+
raise ValueError(
|
|
442
|
+
f"Output path {file_path} must be within static directory {self.static_dir}"
|
|
443
|
+
) from e
|
|
444
|
+
|
|
445
|
+
return file_path
|
|
446
|
+
|
|
447
|
+
def __str__(self) -> str:
|
|
448
|
+
return f"{self.name}: {self.description}"
|
|
449
|
+
|
|
450
|
+
def __repr__(self) -> str:
|
|
451
|
+
return f"<{self.__class__.__name__}(name='{self.name}')>"
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
@dataclass
|
|
455
|
+
class ToolInfo:
|
|
456
|
+
"""Information about a discovered tool."""
|
|
457
|
+
class_name: str
|
|
458
|
+
module_name: str
|
|
459
|
+
description: str
|
|
460
|
+
tool_name: str
|
|
461
|
+
file_path: str
|
|
462
|
+
args_schema: Optional[Dict[str, Any]] = None
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
# Tool Registry for easy tool management
|
|
466
|
+
class ToolRegistry:
|
|
467
|
+
"""Registry for managing available tools."""
|
|
468
|
+
|
|
469
|
+
def __init__(self):
|
|
470
|
+
self._tools = {}
|
|
471
|
+
self.tools_package_path = 'parrot/tools'
|
|
472
|
+
self.discovered_tools: Dict[str, ToolInfo] = {}
|
|
473
|
+
self.loaded_classes: Dict[str, Type[AbstractTool]] = {}
|
|
474
|
+
|
|
475
|
+
def _create_tool_info(
|
|
476
|
+
self,
|
|
477
|
+
cls: Type[AbstractTool],
|
|
478
|
+
class_name: str,
|
|
479
|
+
module_name: str,
|
|
480
|
+
file_path: str
|
|
481
|
+
) -> ToolInfo:
|
|
482
|
+
"""Create ToolInfo object from a tool class."""
|
|
483
|
+
try:
|
|
484
|
+
# Get tool description
|
|
485
|
+
description = getattr(cls, 'description', cls.__doc__ or 'No description available')
|
|
486
|
+
|
|
487
|
+
# Get tool name
|
|
488
|
+
tool_name = getattr(cls, 'name', class_name)
|
|
489
|
+
|
|
490
|
+
# Get args schema if available
|
|
491
|
+
args_schema = None
|
|
492
|
+
if hasattr(cls, 'args_schema') and cls.args_schema:
|
|
493
|
+
try:
|
|
494
|
+
args_schema = cls.args_schema.model_json_schema()
|
|
495
|
+
except Exception as e:
|
|
496
|
+
logging.debug(f"Could not get schema for {class_name}: {e}")
|
|
497
|
+
|
|
498
|
+
return ToolInfo(
|
|
499
|
+
class_name=class_name,
|
|
500
|
+
module_name=module_name,
|
|
501
|
+
description=description,
|
|
502
|
+
tool_name=tool_name,
|
|
503
|
+
file_path=file_path,
|
|
504
|
+
args_schema=args_schema
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
except Exception as e:
|
|
508
|
+
logging.error(f"Error creating tool info for {class_name}: {e}")
|
|
509
|
+
return ToolInfo(
|
|
510
|
+
class_name=class_name,
|
|
511
|
+
module_name=module_name,
|
|
512
|
+
description="Error loading description",
|
|
513
|
+
tool_name=class_name,
|
|
514
|
+
file_path=file_path
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
def _process_python_file(self, file_path: Path, tools_dir: Path) -> None:
|
|
518
|
+
"""Process a single Python file to find tool classes."""
|
|
519
|
+
try:
|
|
520
|
+
# Get relative path from tools directory
|
|
521
|
+
relative_to_tools = file_path.relative_to(tools_dir)
|
|
522
|
+
# Build module name using the configured tools_package_path
|
|
523
|
+
base_package = self.tools_package_path.replace('/', '.') # 'parrot.tools'
|
|
524
|
+
|
|
525
|
+
# Add any subdirectories and filename
|
|
526
|
+
if relative_to_tools.parent != Path('.'):
|
|
527
|
+
# Handle subdirectories: e.g., 'analysis/correlation.py' -> 'parrot.tools.analysis.correlation'
|
|
528
|
+
subpath = str(relative_to_tools.parent).replace(os.sep, '.')
|
|
529
|
+
module_name = f"{base_package}.{subpath}.{relative_to_tools.stem}"
|
|
530
|
+
else:
|
|
531
|
+
# Direct file in tools directory: 'google.py' -> 'parrot.tools.google'
|
|
532
|
+
module_name = f"{base_package}.{relative_to_tools.stem}"
|
|
533
|
+
|
|
534
|
+
logging.debug(f"Processing module: {module_name}")
|
|
535
|
+
|
|
536
|
+
# Import the module
|
|
537
|
+
module = importlib.import_module(module_name)
|
|
538
|
+
|
|
539
|
+
# Find tool classes in the module
|
|
540
|
+
for name, obj in inspect.getmembers(module, inspect.isclass):
|
|
541
|
+
if issubclass(obj, AbstractTool):
|
|
542
|
+
if name == 'AbstractTool':
|
|
543
|
+
continue
|
|
544
|
+
tool_info = self._create_tool_info(
|
|
545
|
+
obj,
|
|
546
|
+
name,
|
|
547
|
+
module_name,
|
|
548
|
+
str(file_path)
|
|
549
|
+
)
|
|
550
|
+
self.discovered_tools[name] = tool_info
|
|
551
|
+
self.loaded_classes[name] = obj
|
|
552
|
+
logging.debug(f"Found tool: {name}")
|
|
553
|
+
|
|
554
|
+
except Exception as e:
|
|
555
|
+
logging.error(f"Error processing {file_path}: {e}")
|
|
556
|
+
|
|
557
|
+
def discover_tools(self) -> Dict[str, ToolInfo]:
|
|
558
|
+
"""
|
|
559
|
+
Discover all tool classes in the tools package.
|
|
560
|
+
|
|
561
|
+
Returns:
|
|
562
|
+
Dict mapping class names to ToolInfo objects
|
|
563
|
+
"""
|
|
564
|
+
tools_dir = Path(self.tools_package_path).resolve()
|
|
565
|
+
if not tools_dir.exists():
|
|
566
|
+
logging.warning(f"Tools directory '{tools_dir}' does not exist")
|
|
567
|
+
return {}
|
|
568
|
+
|
|
569
|
+
# Clear previous discoveries
|
|
570
|
+
self.discovered_tools.clear()
|
|
571
|
+
self.loaded_classes.clear()
|
|
572
|
+
|
|
573
|
+
# walk through the tools directory and find all .py files
|
|
574
|
+
for file_path in tools_dir.rglob('*.py'):
|
|
575
|
+
if file_path.name.startswith('_'):
|
|
576
|
+
continue
|
|
577
|
+
self._process_python_file(file_path, tools_dir)
|
|
578
|
+
|
|
579
|
+
logging.info(f"Discovered {len(self.discovered_tools)} tools")
|
|
580
|
+
return self.discovered_tools
|
|
581
|
+
|
|
582
|
+
def register_toolkit(self, toolkit: Type[AbstractTool], prefix: str = ""):
|
|
583
|
+
"""
|
|
584
|
+
Register all tools from a toolkit in a tool registry.
|
|
585
|
+
|
|
586
|
+
Args:
|
|
587
|
+
registry: Tool registry instance
|
|
588
|
+
toolkit: Toolkit instance
|
|
589
|
+
prefix: Optional prefix for tool names
|
|
590
|
+
"""
|
|
591
|
+
tools = toolkit.get_tools()
|
|
592
|
+
for tool in tools:
|
|
593
|
+
tool_name = f"{prefix}{tool.name}" if prefix else tool.name
|
|
594
|
+
self.register(tool.__class__, tool_name)
|
|
595
|
+
|
|
596
|
+
def register(self, tool_class: Type[AbstractTool], name: Optional[str] = None):
|
|
597
|
+
"""Register a tool class."""
|
|
598
|
+
tool_name = name or tool_class.name or tool_class.__name__
|
|
599
|
+
self._tools[tool_name] = tool_class
|
|
600
|
+
|
|
601
|
+
def register_by_name(self, tool_name: str):
|
|
602
|
+
"""Register a tool class by its name."""
|
|
603
|
+
if tool_name in self._tools:
|
|
604
|
+
raise ValueError(f"Tool '{tool_name}' is already registered")
|
|
605
|
+
# use importlib to dynamically import the tool class
|
|
606
|
+
file_name = tool_name.lower().replace('tool', '')
|
|
607
|
+
try:
|
|
608
|
+
module = __import__(f"parrot.tools.{file_name}", fromlist=[tool_name])
|
|
609
|
+
tool_class = getattr(module, tool_name)
|
|
610
|
+
except (ImportError, AttributeError) as e:
|
|
611
|
+
raise ValueError(f"Could not import tool '{tool_name}': {e}")
|
|
612
|
+
if not issubclass(tool_class, AbstractTool):
|
|
613
|
+
raise ValueError(f"Tool '{tool_name}' must be a subclass of AbstractTool")
|
|
614
|
+
if tool_name in self._tools:
|
|
615
|
+
raise ValueError(f"Tool '{tool_name}' is already registered")
|
|
616
|
+
# Register the tool class
|
|
617
|
+
self._tools[tool_name] = tool_class
|
|
618
|
+
|
|
619
|
+
def get_tool(self, name: str, **kwargs) -> AbstractTool:
|
|
620
|
+
"""Get an instance of a tool by name."""
|
|
621
|
+
if name not in self._tools:
|
|
622
|
+
raise ValueError(f"Tool '{name}' not found in registry")
|
|
623
|
+
|
|
624
|
+
tool_class = self._tools[name]
|
|
625
|
+
return tool_class(**kwargs)
|
|
626
|
+
|
|
627
|
+
def list_tools(self) -> Dict[str, str]:
|
|
628
|
+
"""List all registered tools with their descriptions."""
|
|
629
|
+
return {
|
|
630
|
+
name: getattr(tool_class, 'description', 'No description')
|
|
631
|
+
for name, tool_class in self._tools.items()
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
def get_all_schemas(self) -> Dict[str, Dict[str, Any]]:
|
|
635
|
+
"""Get schemas for all registered tools."""
|
|
636
|
+
schemas = {}
|
|
637
|
+
for name, tool_class in self._tools.items():
|
|
638
|
+
try:
|
|
639
|
+
# Create a temporary instance to get schema
|
|
640
|
+
temp_instance = tool_class()
|
|
641
|
+
schemas[name] = temp_instance.get_tool_schema()
|
|
642
|
+
except Exception as e:
|
|
643
|
+
logging.error(f"Error getting schema for tool {name}: {e}")
|
|
644
|
+
return schemas
|