ai-parrot 0.17.2__cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentui/.prettierrc +15 -0
- agentui/QUICKSTART.md +272 -0
- agentui/README.md +59 -0
- agentui/env.example +16 -0
- agentui/jsconfig.json +14 -0
- agentui/package-lock.json +4242 -0
- agentui/package.json +34 -0
- agentui/scripts/postinstall/apply-patches.mjs +260 -0
- agentui/src/app.css +61 -0
- agentui/src/app.d.ts +13 -0
- agentui/src/app.html +12 -0
- agentui/src/components/LoadingSpinner.svelte +64 -0
- agentui/src/components/ThemeSwitcher.svelte +159 -0
- agentui/src/components/index.js +4 -0
- agentui/src/lib/api/bots.ts +60 -0
- agentui/src/lib/api/chat.ts +22 -0
- agentui/src/lib/api/http.ts +25 -0
- agentui/src/lib/components/BotCard.svelte +33 -0
- agentui/src/lib/components/ChatBubble.svelte +63 -0
- agentui/src/lib/components/Toast.svelte +21 -0
- agentui/src/lib/config.ts +20 -0
- agentui/src/lib/stores/auth.svelte.ts +73 -0
- agentui/src/lib/stores/theme.svelte.js +64 -0
- agentui/src/lib/stores/toast.svelte.ts +31 -0
- agentui/src/lib/utils/conversation.ts +39 -0
- agentui/src/routes/+layout.svelte +20 -0
- agentui/src/routes/+page.svelte +232 -0
- agentui/src/routes/login/+page.svelte +200 -0
- agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
- agentui/src/routes/talk/[agentId]/+page.ts +7 -0
- agentui/static/README.md +1 -0
- agentui/svelte.config.js +11 -0
- agentui/tailwind.config.ts +53 -0
- agentui/tsconfig.json +3 -0
- agentui/vite.config.ts +10 -0
- ai_parrot-0.17.2.dist-info/METADATA +472 -0
- ai_parrot-0.17.2.dist-info/RECORD +535 -0
- ai_parrot-0.17.2.dist-info/WHEEL +6 -0
- ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
- ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
- ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
- crew-builder/.prettierrc +15 -0
- crew-builder/QUICKSTART.md +259 -0
- crew-builder/README.md +113 -0
- crew-builder/env.example +17 -0
- crew-builder/jsconfig.json +14 -0
- crew-builder/package-lock.json +4182 -0
- crew-builder/package.json +37 -0
- crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
- crew-builder/src/app.css +62 -0
- crew-builder/src/app.d.ts +13 -0
- crew-builder/src/app.html +12 -0
- crew-builder/src/components/LoadingSpinner.svelte +64 -0
- crew-builder/src/components/ThemeSwitcher.svelte +149 -0
- crew-builder/src/components/index.js +9 -0
- crew-builder/src/lib/api/bots.ts +60 -0
- crew-builder/src/lib/api/chat.ts +80 -0
- crew-builder/src/lib/api/client.ts +56 -0
- crew-builder/src/lib/api/crew/crew.ts +136 -0
- crew-builder/src/lib/api/index.ts +5 -0
- crew-builder/src/lib/api/o365/auth.ts +65 -0
- crew-builder/src/lib/auth/auth.ts +54 -0
- crew-builder/src/lib/components/AgentNode.svelte +43 -0
- crew-builder/src/lib/components/BotCard.svelte +33 -0
- crew-builder/src/lib/components/ChatBubble.svelte +67 -0
- crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
- crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
- crew-builder/src/lib/components/JsonViewer.svelte +24 -0
- crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
- crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
- crew-builder/src/lib/components/Toast.svelte +67 -0
- crew-builder/src/lib/components/Toolbar.svelte +157 -0
- crew-builder/src/lib/components/index.ts +10 -0
- crew-builder/src/lib/config.ts +8 -0
- crew-builder/src/lib/stores/auth.svelte.ts +228 -0
- crew-builder/src/lib/stores/crewStore.ts +369 -0
- crew-builder/src/lib/stores/theme.svelte.js +145 -0
- crew-builder/src/lib/stores/toast.svelte.ts +69 -0
- crew-builder/src/lib/utils/conversation.ts +39 -0
- crew-builder/src/lib/utils/markdown.ts +122 -0
- crew-builder/src/lib/utils/talkHistory.ts +47 -0
- crew-builder/src/routes/+layout.svelte +20 -0
- crew-builder/src/routes/+page.svelte +539 -0
- crew-builder/src/routes/agents/+page.svelte +247 -0
- crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
- crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
- crew-builder/src/routes/builder/+page.svelte +204 -0
- crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
- crew-builder/src/routes/crew/ask/+page.ts +1 -0
- crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
- crew-builder/src/routes/login/+page.svelte +197 -0
- crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
- crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
- crew-builder/static/README.md +1 -0
- crew-builder/svelte.config.js +11 -0
- crew-builder/tailwind.config.ts +53 -0
- crew-builder/tsconfig.json +3 -0
- crew-builder/vite.config.ts +10 -0
- mcp_servers/calculator_server.py +309 -0
- parrot/__init__.py +27 -0
- parrot/__pycache__/__init__.cpython-310.pyc +0 -0
- parrot/__pycache__/version.cpython-310.pyc +0 -0
- parrot/_version.py +34 -0
- parrot/a2a/__init__.py +48 -0
- parrot/a2a/client.py +658 -0
- parrot/a2a/discovery.py +89 -0
- parrot/a2a/mixin.py +257 -0
- parrot/a2a/models.py +376 -0
- parrot/a2a/server.py +770 -0
- parrot/agents/__init__.py +29 -0
- parrot/bots/__init__.py +12 -0
- parrot/bots/a2a_agent.py +19 -0
- parrot/bots/abstract.py +3139 -0
- parrot/bots/agent.py +1129 -0
- parrot/bots/basic.py +9 -0
- parrot/bots/chatbot.py +669 -0
- parrot/bots/data.py +1618 -0
- parrot/bots/database/__init__.py +5 -0
- parrot/bots/database/abstract.py +3071 -0
- parrot/bots/database/cache.py +286 -0
- parrot/bots/database/models.py +468 -0
- parrot/bots/database/prompts.py +154 -0
- parrot/bots/database/retries.py +98 -0
- parrot/bots/database/router.py +269 -0
- parrot/bots/database/sql.py +41 -0
- parrot/bots/db/__init__.py +6 -0
- parrot/bots/db/abstract.py +556 -0
- parrot/bots/db/bigquery.py +602 -0
- parrot/bots/db/cache.py +85 -0
- parrot/bots/db/documentdb.py +668 -0
- parrot/bots/db/elastic.py +1014 -0
- parrot/bots/db/influx.py +898 -0
- parrot/bots/db/mock.py +96 -0
- parrot/bots/db/multi.py +783 -0
- parrot/bots/db/prompts.py +185 -0
- parrot/bots/db/sql.py +1255 -0
- parrot/bots/db/tools.py +212 -0
- parrot/bots/document.py +680 -0
- parrot/bots/hrbot.py +15 -0
- parrot/bots/kb.py +170 -0
- parrot/bots/mcp.py +36 -0
- parrot/bots/orchestration/README.md +463 -0
- parrot/bots/orchestration/__init__.py +1 -0
- parrot/bots/orchestration/agent.py +155 -0
- parrot/bots/orchestration/crew.py +3330 -0
- parrot/bots/orchestration/fsm.py +1179 -0
- parrot/bots/orchestration/hr.py +434 -0
- parrot/bots/orchestration/storage/__init__.py +4 -0
- parrot/bots/orchestration/storage/memory.py +100 -0
- parrot/bots/orchestration/storage/mixin.py +119 -0
- parrot/bots/orchestration/verify.py +202 -0
- parrot/bots/product.py +204 -0
- parrot/bots/prompts/__init__.py +96 -0
- parrot/bots/prompts/agents.py +155 -0
- parrot/bots/prompts/data.py +216 -0
- parrot/bots/prompts/output_generation.py +8 -0
- parrot/bots/scraper/__init__.py +3 -0
- parrot/bots/scraper/models.py +122 -0
- parrot/bots/scraper/scraper.py +1173 -0
- parrot/bots/scraper/templates.py +115 -0
- parrot/bots/stores/__init__.py +5 -0
- parrot/bots/stores/local.py +172 -0
- parrot/bots/webdev.py +81 -0
- parrot/cli.py +17 -0
- parrot/clients/__init__.py +16 -0
- parrot/clients/base.py +1491 -0
- parrot/clients/claude.py +1191 -0
- parrot/clients/factory.py +129 -0
- parrot/clients/google.py +4567 -0
- parrot/clients/gpt.py +1975 -0
- parrot/clients/grok.py +432 -0
- parrot/clients/groq.py +986 -0
- parrot/clients/hf.py +582 -0
- parrot/clients/models.py +18 -0
- parrot/conf.py +395 -0
- parrot/embeddings/__init__.py +9 -0
- parrot/embeddings/base.py +157 -0
- parrot/embeddings/google.py +98 -0
- parrot/embeddings/huggingface.py +74 -0
- parrot/embeddings/openai.py +84 -0
- parrot/embeddings/processor.py +88 -0
- parrot/exceptions.c +13868 -0
- parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/exceptions.pxd +22 -0
- parrot/exceptions.pxi +15 -0
- parrot/exceptions.pyx +44 -0
- parrot/generators/__init__.py +29 -0
- parrot/generators/base.py +200 -0
- parrot/generators/html.py +293 -0
- parrot/generators/react.py +205 -0
- parrot/generators/streamlit.py +203 -0
- parrot/generators/template.py +105 -0
- parrot/handlers/__init__.py +4 -0
- parrot/handlers/agent.py +861 -0
- parrot/handlers/agents/__init__.py +1 -0
- parrot/handlers/agents/abstract.py +900 -0
- parrot/handlers/bots.py +338 -0
- parrot/handlers/chat.py +915 -0
- parrot/handlers/creation.sql +192 -0
- parrot/handlers/crew/ARCHITECTURE.md +362 -0
- parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
- parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
- parrot/handlers/crew/__init__.py +0 -0
- parrot/handlers/crew/handler.py +801 -0
- parrot/handlers/crew/models.py +229 -0
- parrot/handlers/crew/redis_persistence.py +523 -0
- parrot/handlers/jobs/__init__.py +10 -0
- parrot/handlers/jobs/job.py +384 -0
- parrot/handlers/jobs/mixin.py +627 -0
- parrot/handlers/jobs/models.py +115 -0
- parrot/handlers/jobs/worker.py +31 -0
- parrot/handlers/models.py +596 -0
- parrot/handlers/o365_auth.py +105 -0
- parrot/handlers/stream.py +337 -0
- parrot/interfaces/__init__.py +6 -0
- parrot/interfaces/aws.py +143 -0
- parrot/interfaces/credentials.py +113 -0
- parrot/interfaces/database.py +27 -0
- parrot/interfaces/google.py +1123 -0
- parrot/interfaces/hierarchy.py +1227 -0
- parrot/interfaces/http.py +651 -0
- parrot/interfaces/images/__init__.py +0 -0
- parrot/interfaces/images/plugins/__init__.py +24 -0
- parrot/interfaces/images/plugins/abstract.py +58 -0
- parrot/interfaces/images/plugins/analisys.py +148 -0
- parrot/interfaces/images/plugins/classify.py +150 -0
- parrot/interfaces/images/plugins/classifybase.py +182 -0
- parrot/interfaces/images/plugins/detect.py +150 -0
- parrot/interfaces/images/plugins/exif.py +1103 -0
- parrot/interfaces/images/plugins/hash.py +52 -0
- parrot/interfaces/images/plugins/vision.py +104 -0
- parrot/interfaces/images/plugins/yolo.py +66 -0
- parrot/interfaces/images/plugins/zerodetect.py +197 -0
- parrot/interfaces/o365.py +978 -0
- parrot/interfaces/onedrive.py +822 -0
- parrot/interfaces/sharepoint.py +1435 -0
- parrot/interfaces/soap.py +257 -0
- parrot/loaders/__init__.py +8 -0
- parrot/loaders/abstract.py +1131 -0
- parrot/loaders/audio.py +199 -0
- parrot/loaders/basepdf.py +53 -0
- parrot/loaders/basevideo.py +1568 -0
- parrot/loaders/csv.py +409 -0
- parrot/loaders/docx.py +116 -0
- parrot/loaders/epubloader.py +316 -0
- parrot/loaders/excel.py +199 -0
- parrot/loaders/factory.py +55 -0
- parrot/loaders/files/__init__.py +0 -0
- parrot/loaders/files/abstract.py +39 -0
- parrot/loaders/files/html.py +26 -0
- parrot/loaders/files/text.py +63 -0
- parrot/loaders/html.py +152 -0
- parrot/loaders/markdown.py +442 -0
- parrot/loaders/pdf.py +373 -0
- parrot/loaders/pdfmark.py +320 -0
- parrot/loaders/pdftables.py +506 -0
- parrot/loaders/ppt.py +476 -0
- parrot/loaders/qa.py +63 -0
- parrot/loaders/splitters/__init__.py +10 -0
- parrot/loaders/splitters/base.py +138 -0
- parrot/loaders/splitters/md.py +228 -0
- parrot/loaders/splitters/token.py +143 -0
- parrot/loaders/txt.py +26 -0
- parrot/loaders/video.py +89 -0
- parrot/loaders/videolocal.py +218 -0
- parrot/loaders/videounderstanding.py +377 -0
- parrot/loaders/vimeo.py +167 -0
- parrot/loaders/web.py +599 -0
- parrot/loaders/youtube.py +504 -0
- parrot/manager/__init__.py +5 -0
- parrot/manager/manager.py +1030 -0
- parrot/mcp/__init__.py +28 -0
- parrot/mcp/adapter.py +105 -0
- parrot/mcp/cli.py +174 -0
- parrot/mcp/client.py +119 -0
- parrot/mcp/config.py +75 -0
- parrot/mcp/integration.py +842 -0
- parrot/mcp/oauth.py +933 -0
- parrot/mcp/server.py +225 -0
- parrot/mcp/transports/__init__.py +3 -0
- parrot/mcp/transports/base.py +279 -0
- parrot/mcp/transports/grpc_session.py +163 -0
- parrot/mcp/transports/http.py +312 -0
- parrot/mcp/transports/mcp.proto +108 -0
- parrot/mcp/transports/quic.py +1082 -0
- parrot/mcp/transports/sse.py +330 -0
- parrot/mcp/transports/stdio.py +309 -0
- parrot/mcp/transports/unix.py +395 -0
- parrot/mcp/transports/websocket.py +547 -0
- parrot/memory/__init__.py +16 -0
- parrot/memory/abstract.py +209 -0
- parrot/memory/agent.py +32 -0
- parrot/memory/cache.py +175 -0
- parrot/memory/core.py +555 -0
- parrot/memory/file.py +153 -0
- parrot/memory/mem.py +131 -0
- parrot/memory/redis.py +613 -0
- parrot/models/__init__.py +46 -0
- parrot/models/basic.py +118 -0
- parrot/models/compliance.py +208 -0
- parrot/models/crew.py +395 -0
- parrot/models/detections.py +654 -0
- parrot/models/generation.py +85 -0
- parrot/models/google.py +223 -0
- parrot/models/groq.py +23 -0
- parrot/models/openai.py +30 -0
- parrot/models/outputs.py +285 -0
- parrot/models/responses.py +938 -0
- parrot/notifications/__init__.py +743 -0
- parrot/openapi/__init__.py +3 -0
- parrot/openapi/components.yaml +641 -0
- parrot/openapi/config.py +322 -0
- parrot/outputs/__init__.py +32 -0
- parrot/outputs/formats/__init__.py +108 -0
- parrot/outputs/formats/altair.py +359 -0
- parrot/outputs/formats/application.py +122 -0
- parrot/outputs/formats/base.py +351 -0
- parrot/outputs/formats/bokeh.py +356 -0
- parrot/outputs/formats/card.py +424 -0
- parrot/outputs/formats/chart.py +436 -0
- parrot/outputs/formats/d3.py +255 -0
- parrot/outputs/formats/echarts.py +310 -0
- parrot/outputs/formats/generators/__init__.py +0 -0
- parrot/outputs/formats/generators/abstract.py +61 -0
- parrot/outputs/formats/generators/panel.py +145 -0
- parrot/outputs/formats/generators/streamlit.py +86 -0
- parrot/outputs/formats/generators/terminal.py +63 -0
- parrot/outputs/formats/holoviews.py +310 -0
- parrot/outputs/formats/html.py +147 -0
- parrot/outputs/formats/jinja2.py +46 -0
- parrot/outputs/formats/json.py +87 -0
- parrot/outputs/formats/map.py +933 -0
- parrot/outputs/formats/markdown.py +172 -0
- parrot/outputs/formats/matplotlib.py +237 -0
- parrot/outputs/formats/mixins/__init__.py +0 -0
- parrot/outputs/formats/mixins/emaps.py +855 -0
- parrot/outputs/formats/plotly.py +341 -0
- parrot/outputs/formats/seaborn.py +310 -0
- parrot/outputs/formats/table.py +397 -0
- parrot/outputs/formats/template_report.py +138 -0
- parrot/outputs/formats/yaml.py +125 -0
- parrot/outputs/formatter.py +152 -0
- parrot/outputs/templates/__init__.py +95 -0
- parrot/pipelines/__init__.py +0 -0
- parrot/pipelines/abstract.py +210 -0
- parrot/pipelines/detector.py +124 -0
- parrot/pipelines/models.py +90 -0
- parrot/pipelines/planogram.py +3002 -0
- parrot/pipelines/table.sql +97 -0
- parrot/plugins/__init__.py +106 -0
- parrot/plugins/importer.py +80 -0
- parrot/py.typed +0 -0
- parrot/registry/__init__.py +18 -0
- parrot/registry/registry.py +594 -0
- parrot/scheduler/__init__.py +1189 -0
- parrot/scheduler/models.py +60 -0
- parrot/security/__init__.py +16 -0
- parrot/security/prompt_injection.py +268 -0
- parrot/security/security_events.sql +25 -0
- parrot/services/__init__.py +1 -0
- parrot/services/mcp/__init__.py +8 -0
- parrot/services/mcp/config.py +13 -0
- parrot/services/mcp/server.py +295 -0
- parrot/services/o365_remote_auth.py +235 -0
- parrot/stores/__init__.py +7 -0
- parrot/stores/abstract.py +352 -0
- parrot/stores/arango.py +1090 -0
- parrot/stores/bigquery.py +1377 -0
- parrot/stores/cache.py +106 -0
- parrot/stores/empty.py +10 -0
- parrot/stores/faiss_store.py +1157 -0
- parrot/stores/kb/__init__.py +9 -0
- parrot/stores/kb/abstract.py +68 -0
- parrot/stores/kb/cache.py +165 -0
- parrot/stores/kb/doc.py +325 -0
- parrot/stores/kb/hierarchy.py +346 -0
- parrot/stores/kb/local.py +457 -0
- parrot/stores/kb/prompt.py +28 -0
- parrot/stores/kb/redis.py +659 -0
- parrot/stores/kb/store.py +115 -0
- parrot/stores/kb/user.py +374 -0
- parrot/stores/models.py +59 -0
- parrot/stores/pgvector.py +3 -0
- parrot/stores/postgres.py +2853 -0
- parrot/stores/utils/__init__.py +0 -0
- parrot/stores/utils/chunking.py +197 -0
- parrot/telemetry/__init__.py +3 -0
- parrot/telemetry/mixin.py +111 -0
- parrot/template/__init__.py +3 -0
- parrot/template/engine.py +259 -0
- parrot/tools/__init__.py +23 -0
- parrot/tools/abstract.py +644 -0
- parrot/tools/agent.py +363 -0
- parrot/tools/arangodbsearch.py +537 -0
- parrot/tools/arxiv_tool.py +188 -0
- parrot/tools/calculator/__init__.py +3 -0
- parrot/tools/calculator/operations/__init__.py +38 -0
- parrot/tools/calculator/operations/calculus.py +80 -0
- parrot/tools/calculator/operations/statistics.py +76 -0
- parrot/tools/calculator/tool.py +150 -0
- parrot/tools/cloudwatch.py +988 -0
- parrot/tools/codeinterpreter/__init__.py +127 -0
- parrot/tools/codeinterpreter/executor.py +371 -0
- parrot/tools/codeinterpreter/internals.py +473 -0
- parrot/tools/codeinterpreter/models.py +643 -0
- parrot/tools/codeinterpreter/prompts.py +224 -0
- parrot/tools/codeinterpreter/tool.py +664 -0
- parrot/tools/company_info/__init__.py +6 -0
- parrot/tools/company_info/tool.py +1138 -0
- parrot/tools/correlationanalysis.py +437 -0
- parrot/tools/database/abstract.py +286 -0
- parrot/tools/database/bq.py +115 -0
- parrot/tools/database/cache.py +284 -0
- parrot/tools/database/models.py +95 -0
- parrot/tools/database/pg.py +343 -0
- parrot/tools/databasequery.py +1159 -0
- parrot/tools/db.py +1800 -0
- parrot/tools/ddgo.py +370 -0
- parrot/tools/decorators.py +271 -0
- parrot/tools/dftohtml.py +282 -0
- parrot/tools/document.py +549 -0
- parrot/tools/ecs.py +819 -0
- parrot/tools/edareport.py +368 -0
- parrot/tools/elasticsearch.py +1049 -0
- parrot/tools/employees.py +462 -0
- parrot/tools/epson/__init__.py +96 -0
- parrot/tools/excel.py +683 -0
- parrot/tools/file/__init__.py +13 -0
- parrot/tools/file/abstract.py +76 -0
- parrot/tools/file/gcs.py +378 -0
- parrot/tools/file/local.py +284 -0
- parrot/tools/file/s3.py +511 -0
- parrot/tools/file/tmp.py +309 -0
- parrot/tools/file/tool.py +501 -0
- parrot/tools/file_reader.py +129 -0
- parrot/tools/flowtask/__init__.py +19 -0
- parrot/tools/flowtask/tool.py +761 -0
- parrot/tools/gittoolkit.py +508 -0
- parrot/tools/google/__init__.py +18 -0
- parrot/tools/google/base.py +169 -0
- parrot/tools/google/tools.py +1251 -0
- parrot/tools/googlelocation.py +5 -0
- parrot/tools/googleroutes.py +5 -0
- parrot/tools/googlesearch.py +5 -0
- parrot/tools/googlesitesearch.py +5 -0
- parrot/tools/googlevoice.py +2 -0
- parrot/tools/gvoice.py +695 -0
- parrot/tools/ibisworld/README.md +225 -0
- parrot/tools/ibisworld/__init__.py +11 -0
- parrot/tools/ibisworld/tool.py +366 -0
- parrot/tools/jiratoolkit.py +1718 -0
- parrot/tools/manager.py +1098 -0
- parrot/tools/math.py +152 -0
- parrot/tools/metadata.py +476 -0
- parrot/tools/msteams.py +1621 -0
- parrot/tools/msword.py +635 -0
- parrot/tools/multidb.py +580 -0
- parrot/tools/multistoresearch.py +369 -0
- parrot/tools/networkninja.py +167 -0
- parrot/tools/nextstop/__init__.py +4 -0
- parrot/tools/nextstop/base.py +286 -0
- parrot/tools/nextstop/employee.py +733 -0
- parrot/tools/nextstop/store.py +462 -0
- parrot/tools/notification.py +435 -0
- parrot/tools/o365/__init__.py +42 -0
- parrot/tools/o365/base.py +295 -0
- parrot/tools/o365/bundle.py +522 -0
- parrot/tools/o365/events.py +554 -0
- parrot/tools/o365/mail.py +992 -0
- parrot/tools/o365/onedrive.py +497 -0
- parrot/tools/o365/sharepoint.py +641 -0
- parrot/tools/openapi_toolkit.py +904 -0
- parrot/tools/openweather.py +527 -0
- parrot/tools/pdfprint.py +1001 -0
- parrot/tools/powerbi.py +518 -0
- parrot/tools/powerpoint.py +1113 -0
- parrot/tools/pricestool.py +146 -0
- parrot/tools/products/__init__.py +246 -0
- parrot/tools/prophet_tool.py +171 -0
- parrot/tools/pythonpandas.py +630 -0
- parrot/tools/pythonrepl.py +910 -0
- parrot/tools/qsource.py +436 -0
- parrot/tools/querytoolkit.py +395 -0
- parrot/tools/quickeda.py +827 -0
- parrot/tools/resttool.py +553 -0
- parrot/tools/retail/__init__.py +0 -0
- parrot/tools/retail/bby.py +528 -0
- parrot/tools/sandboxtool.py +703 -0
- parrot/tools/sassie/__init__.py +352 -0
- parrot/tools/scraping/__init__.py +7 -0
- parrot/tools/scraping/docs/select.md +466 -0
- parrot/tools/scraping/documentation.md +1278 -0
- parrot/tools/scraping/driver.py +436 -0
- parrot/tools/scraping/models.py +576 -0
- parrot/tools/scraping/options.py +85 -0
- parrot/tools/scraping/orchestrator.py +517 -0
- parrot/tools/scraping/readme.md +740 -0
- parrot/tools/scraping/tool.py +3115 -0
- parrot/tools/seasonaldetection.py +642 -0
- parrot/tools/shell_tool/__init__.py +5 -0
- parrot/tools/shell_tool/actions.py +408 -0
- parrot/tools/shell_tool/engine.py +155 -0
- parrot/tools/shell_tool/models.py +322 -0
- parrot/tools/shell_tool/tool.py +442 -0
- parrot/tools/site_search.py +214 -0
- parrot/tools/textfile.py +418 -0
- parrot/tools/think.py +378 -0
- parrot/tools/toolkit.py +298 -0
- parrot/tools/webapp_tool.py +187 -0
- parrot/tools/whatif.py +1279 -0
- parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
- parrot/tools/workday/__init__.py +6 -0
- parrot/tools/workday/models.py +1389 -0
- parrot/tools/workday/tool.py +1293 -0
- parrot/tools/yfinance_tool.py +306 -0
- parrot/tools/zipcode.py +217 -0
- parrot/utils/__init__.py +2 -0
- parrot/utils/helpers.py +73 -0
- parrot/utils/parsers/__init__.py +5 -0
- parrot/utils/parsers/toml.c +12078 -0
- parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/parsers/toml.pyx +21 -0
- parrot/utils/toml.py +11 -0
- parrot/utils/types.cpp +20936 -0
- parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/types.pyx +213 -0
- parrot/utils/uv.py +11 -0
- parrot/version.py +10 -0
- parrot/yaml-rs/Cargo.lock +350 -0
- parrot/yaml-rs/Cargo.toml +19 -0
- parrot/yaml-rs/pyproject.toml +19 -0
- parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
- parrot/yaml-rs/src/lib.rs +222 -0
- requirements/docker-compose.yml +24 -0
- requirements/requirements-dev.txt +21 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
from typing import Any, List, Dict, Optional, Tuple, Type, TYPE_CHECKING
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
import contextlib
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from dataclasses import is_dataclass, asdict
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import numpy as np
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
import orjson
|
|
10
|
+
from datamodel.parsers.json import json_encoder # pylint: disable=E0611 # noqa
|
|
11
|
+
from pygments import highlight
|
|
12
|
+
from pygments.lexers.data import JsonLexer
|
|
13
|
+
from pygments.formatters.html import HtmlFormatter
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from ...tools.pythonpandas import PythonPandasTool
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BaseRenderer(ABC):
|
|
19
|
+
"""Base class for output renderers."""
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def get_expected_content_type(cls) -> Type:
|
|
23
|
+
"""
|
|
24
|
+
Define what type of content this renderer expects.
|
|
25
|
+
Override in subclasses to specify expected type.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Type: The expected type (str, pd.DataFrame, dict, etc.)
|
|
29
|
+
"""
|
|
30
|
+
return str
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def _get_content(cls, response: Any) -> Any:
|
|
34
|
+
"""
|
|
35
|
+
Extract content from response based on expected type.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
response: AIMessage response object
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Content in the expected type
|
|
42
|
+
"""
|
|
43
|
+
expected_type = cls.get_expected_content_type()
|
|
44
|
+
|
|
45
|
+
# First, try to get the output attribute (structured data)
|
|
46
|
+
if hasattr(response, 'output') and response.output is not None:
|
|
47
|
+
output = response.output
|
|
48
|
+
|
|
49
|
+
# If output matches expected type, return it
|
|
50
|
+
if isinstance(output, expected_type):
|
|
51
|
+
return output
|
|
52
|
+
|
|
53
|
+
# Special handling for DataFrames
|
|
54
|
+
if expected_type == pd.DataFrame:
|
|
55
|
+
if isinstance(output, pd.DataFrame):
|
|
56
|
+
return output
|
|
57
|
+
# Try to convert dict/list to DataFrame
|
|
58
|
+
elif isinstance(output, (dict, list)):
|
|
59
|
+
with contextlib.suppress(Exception):
|
|
60
|
+
return pd.DataFrame(output)
|
|
61
|
+
|
|
62
|
+
# Fallback to string extraction for code-based renderers
|
|
63
|
+
if expected_type == str:
|
|
64
|
+
# If response has 'response' attribute (string content)
|
|
65
|
+
if hasattr(response, 'response') and response.response:
|
|
66
|
+
return response.response
|
|
67
|
+
|
|
68
|
+
# Try content attribute
|
|
69
|
+
if hasattr(response, 'content'):
|
|
70
|
+
return response.content
|
|
71
|
+
|
|
72
|
+
# Try to_text property
|
|
73
|
+
if hasattr(response, 'to_text'):
|
|
74
|
+
return response.to_text
|
|
75
|
+
|
|
76
|
+
# Try output as string
|
|
77
|
+
if hasattr(response, 'output'):
|
|
78
|
+
output = response.output
|
|
79
|
+
return output if isinstance(output, str) else str(output)
|
|
80
|
+
|
|
81
|
+
# Last resort: string conversion
|
|
82
|
+
return str(response)
|
|
83
|
+
|
|
84
|
+
def execute_code(
|
|
85
|
+
self,
|
|
86
|
+
code: str,
|
|
87
|
+
pandas_tool: "PythonPandasTool | None" = None,
|
|
88
|
+
execution_state: Optional[Dict[str, Any]] = None,
|
|
89
|
+
extra_namespace: Optional[Dict[str, Any]] = None,
|
|
90
|
+
**kwargs,
|
|
91
|
+
) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
|
|
92
|
+
"""Execute code within the PythonPandasTool or fallback namespace."""
|
|
93
|
+
if tool := pandas_tool:
|
|
94
|
+
try:
|
|
95
|
+
tool.execute_sync(code, debug=kwargs.get('debug', False))
|
|
96
|
+
return tool.locals, None
|
|
97
|
+
except Exception as exc:
|
|
98
|
+
return None, f"Execution error: {exc}"
|
|
99
|
+
|
|
100
|
+
namespace: Dict[str, Any] = {'pd': pd, 'np': np}
|
|
101
|
+
if extra_namespace:
|
|
102
|
+
namespace |= extra_namespace
|
|
103
|
+
|
|
104
|
+
locals_dict: Dict[str, Any] = {}
|
|
105
|
+
if execution_state:
|
|
106
|
+
namespace.update(execution_state.get('dataframes', {}))
|
|
107
|
+
namespace.update(execution_state.get('execution_results', {}))
|
|
108
|
+
namespace.update(execution_state.get('variables', {}))
|
|
109
|
+
globals_state = execution_state.get('globals') or {}
|
|
110
|
+
if isinstance(globals_state, dict):
|
|
111
|
+
namespace.update(globals_state)
|
|
112
|
+
locals_state = execution_state.get('locals') or {}
|
|
113
|
+
if isinstance(locals_state, dict):
|
|
114
|
+
locals_dict = locals_state.copy()
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
exec(code, namespace, locals_dict)
|
|
118
|
+
combined: Dict[str, Any] = {}
|
|
119
|
+
combined |= namespace
|
|
120
|
+
combined.update(locals_dict)
|
|
121
|
+
return combined, None
|
|
122
|
+
except Exception as exc:
|
|
123
|
+
return None, f"Execution error: {exc}"
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def _create_tools_list(tool_calls: List[Any]) -> List[Dict[str, str]]:
|
|
127
|
+
"""Create a list for tool calls."""
|
|
128
|
+
calls = []
|
|
129
|
+
for idx, tool in enumerate(tool_calls, 1):
|
|
130
|
+
name = getattr(tool, 'name', 'Unknown')
|
|
131
|
+
status = getattr(tool, 'status', 'completed')
|
|
132
|
+
calls.append({
|
|
133
|
+
"No.": str(idx),
|
|
134
|
+
"Tool Name": name,
|
|
135
|
+
"Status": status
|
|
136
|
+
})
|
|
137
|
+
return calls
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def _create_sources_list(sources: List[Any]) -> List[Dict[str, str]]:
|
|
141
|
+
"""Create a list for source documents."""
|
|
142
|
+
sources = []
|
|
143
|
+
for idx, source in enumerate(sources, 1):
|
|
144
|
+
# Handle both SourceDocument objects and dict-like sources
|
|
145
|
+
if hasattr(source, 'source'):
|
|
146
|
+
source_name = source.source
|
|
147
|
+
elif isinstance(source, dict):
|
|
148
|
+
source_name = source.get('source', 'Unknown')
|
|
149
|
+
else:
|
|
150
|
+
source_name = str(source)
|
|
151
|
+
if hasattr(source, 'score'):
|
|
152
|
+
score = source.score
|
|
153
|
+
elif isinstance(source, dict):
|
|
154
|
+
score = source.get('score', 'N/A')
|
|
155
|
+
else:
|
|
156
|
+
score = 'N/A'
|
|
157
|
+
sources.append({
|
|
158
|
+
"No.": str(idx),
|
|
159
|
+
"Source": source_name,
|
|
160
|
+
"Score": score,
|
|
161
|
+
})
|
|
162
|
+
return sources
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def _serialize_any(obj: Any) -> Any:
|
|
166
|
+
"""Serialize any Python object to a compatible format"""
|
|
167
|
+
# Pydantic BaseModel
|
|
168
|
+
if hasattr(obj, 'model_dump'):
|
|
169
|
+
return obj.model_dump()
|
|
170
|
+
|
|
171
|
+
# Dataclass
|
|
172
|
+
if hasattr(obj, '__dataclass_fields__'):
|
|
173
|
+
return asdict(obj)
|
|
174
|
+
|
|
175
|
+
# Dict-like
|
|
176
|
+
if hasattr(obj, 'items'):
|
|
177
|
+
return dict(obj)
|
|
178
|
+
|
|
179
|
+
# List-like
|
|
180
|
+
if hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes)):
|
|
181
|
+
return list(obj)
|
|
182
|
+
|
|
183
|
+
# Primitives
|
|
184
|
+
if isinstance(obj, (str, int, float, bool, type(None))):
|
|
185
|
+
return obj
|
|
186
|
+
|
|
187
|
+
# Fallback to string representation
|
|
188
|
+
return str(obj)
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def _clean_data(data: dict) -> dict:
|
|
192
|
+
"""Clean data for Serialization (remove non-serializable types)"""
|
|
193
|
+
cleaned = {}
|
|
194
|
+
for key, value in data.items():
|
|
195
|
+
# Skip None values
|
|
196
|
+
if value is None:
|
|
197
|
+
continue
|
|
198
|
+
|
|
199
|
+
# Handle datetime objects
|
|
200
|
+
if hasattr(value, 'isoformat'):
|
|
201
|
+
cleaned[key] = value.isoformat()
|
|
202
|
+
# Handle Path objects
|
|
203
|
+
elif hasattr(value, '__fspath__'):
|
|
204
|
+
cleaned[key] = str(value)
|
|
205
|
+
# Handle nested dicts
|
|
206
|
+
elif isinstance(value, dict):
|
|
207
|
+
cleaned[key] = BaseRenderer._clean_data(value)
|
|
208
|
+
# Handle lists
|
|
209
|
+
elif isinstance(value, list):
|
|
210
|
+
cleaned[key] = [
|
|
211
|
+
BaseRenderer._clean_data(item) if isinstance(item, dict) else item
|
|
212
|
+
for item in value
|
|
213
|
+
]
|
|
214
|
+
# Primitives
|
|
215
|
+
else:
|
|
216
|
+
cleaned[key] = value
|
|
217
|
+
|
|
218
|
+
return cleaned
|
|
219
|
+
|
|
220
|
+
@staticmethod
|
|
221
|
+
def _prepare_data(response: Any, include_metadata: bool = False) -> dict:
|
|
222
|
+
"""
|
|
223
|
+
Prepare response data for serialization.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
response: AIMessage or any object
|
|
227
|
+
include_metadata: Whether to include full metadata
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Dictionary ready for YAML serialization
|
|
231
|
+
"""
|
|
232
|
+
if not hasattr(response, 'model_dump'):
|
|
233
|
+
# Handle other types
|
|
234
|
+
return BaseRenderer._serialize_any(response)
|
|
235
|
+
# If it's an AIMessage, extract relevant data
|
|
236
|
+
data = response.model_dump(
|
|
237
|
+
exclude_none=True,
|
|
238
|
+
exclude_unset=True
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
if not include_metadata:
|
|
242
|
+
# Return simplified version
|
|
243
|
+
result = {
|
|
244
|
+
'input': data.get('input'),
|
|
245
|
+
'output': data.get('output'),
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# Add essential metadata
|
|
249
|
+
if data.get('model'):
|
|
250
|
+
result['model'] = data['model']
|
|
251
|
+
if data.get('provider'):
|
|
252
|
+
result['provider'] = data['provider']
|
|
253
|
+
if data.get('usage'):
|
|
254
|
+
result['usage'] = data['usage']
|
|
255
|
+
|
|
256
|
+
return result
|
|
257
|
+
|
|
258
|
+
# Full metadata mode
|
|
259
|
+
return BaseRenderer._clean_data(data)
|
|
260
|
+
|
|
261
|
+
def _default_serializer(self, obj: Any) -> Any:
|
|
262
|
+
"""Custom serializer for non-JSON-serializable objects."""
|
|
263
|
+
if isinstance(obj, (datetime, pd.Timestamp)):
|
|
264
|
+
return obj.isoformat()
|
|
265
|
+
if isinstance(obj, pd.DataFrame):
|
|
266
|
+
return obj.to_dict(orient='records')
|
|
267
|
+
if isinstance(obj, set):
|
|
268
|
+
return list(obj)
|
|
269
|
+
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
|
|
270
|
+
|
|
271
|
+
def _extract_data(self, response: Any) -> Any:
|
|
272
|
+
"""
|
|
273
|
+
Extract serializable data based on response content type rules.
|
|
274
|
+
"""
|
|
275
|
+
# 1. Check for PandasAgentResponse (duck typing to avoid circular imports)
|
|
276
|
+
# We check for specific attributes that define a PandasAgentResponse
|
|
277
|
+
output = getattr(response, 'output', None)
|
|
278
|
+
|
|
279
|
+
if output is not None:
|
|
280
|
+
# Handle PandasAgentResponse specifically
|
|
281
|
+
if hasattr(output, 'to_dataframe') and hasattr(output, 'explanation') and hasattr(output, 'data'):
|
|
282
|
+
# response.data is usually a PandasTable
|
|
283
|
+
return output.to_dataframe() if output.data is not None else []
|
|
284
|
+
|
|
285
|
+
# 2. Handle direct DataFrame output
|
|
286
|
+
if isinstance(output, pd.DataFrame):
|
|
287
|
+
return output.to_dict(orient='records')
|
|
288
|
+
|
|
289
|
+
# 3. Handle Pydantic Models
|
|
290
|
+
if isinstance(output, BaseModel):
|
|
291
|
+
return output.model_dump()
|
|
292
|
+
|
|
293
|
+
# 4. Handle Dataclasses
|
|
294
|
+
if is_dataclass(output):
|
|
295
|
+
return asdict(output)
|
|
296
|
+
|
|
297
|
+
# 5. Fallback for unstructured/plain text responses
|
|
298
|
+
# "if there is no 'structured output response', build a JSON with input/output"
|
|
299
|
+
is_structured = getattr(response, 'is_structured', False)
|
|
300
|
+
if not is_structured and output:
|
|
301
|
+
return {
|
|
302
|
+
"input": getattr(response, 'input', ''),
|
|
303
|
+
"output": output,
|
|
304
|
+
"metadata": getattr(response, 'metadata', {})
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return output
|
|
308
|
+
|
|
309
|
+
def _serialize(self, data: Any, indent: Optional[int] = None) -> str:
|
|
310
|
+
"""Serialize data to JSON string using orjson if available."""
|
|
311
|
+
try:
|
|
312
|
+
option = orjson.OPT_INDENT_2 if indent is not None else 0 # pylint: disable=E1101
|
|
313
|
+
# orjson returns bytes, decode to str
|
|
314
|
+
return orjson.dumps( # pylint: disable=E1101
|
|
315
|
+
data,
|
|
316
|
+
default=self._default_serializer,
|
|
317
|
+
option=option
|
|
318
|
+
).decode('utf-8')
|
|
319
|
+
except Exception:
|
|
320
|
+
return json_encoder(
|
|
321
|
+
data
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def _wrap_html(self, content: str) -> str:
|
|
325
|
+
"""Helper to wrap JSON in HTML with highlighting."""
|
|
326
|
+
try:
|
|
327
|
+
formatter = HtmlFormatter(style='default', full=False, noclasses=True)
|
|
328
|
+
highlighted_code = highlight(content, JsonLexer(), formatter)
|
|
329
|
+
return f'<div class="json-response" style="padding:1em; border:1px solid #ddd; border-radius:4px;">{highlighted_code}</div>'
|
|
330
|
+
except ImportError:
|
|
331
|
+
return f'<pre><code class="language-json">{content}</code></pre>'
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@abstractmethod
|
|
335
|
+
async def render(
|
|
336
|
+
self,
|
|
337
|
+
response: Any,
|
|
338
|
+
environment: str = 'terminal',
|
|
339
|
+
export_format: str = 'html',
|
|
340
|
+
include_code: bool = False,
|
|
341
|
+
**kwargs,
|
|
342
|
+
) -> Tuple[Any, Optional[Any]]:
|
|
343
|
+
"""
|
|
344
|
+
Render response in the appropriate format.
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Tuple[Any, Optional[Any]]: (content, wrapped)
|
|
348
|
+
- content: Primary formatted output
|
|
349
|
+
- wrapped: Optional wrapped version (e.g., HTML, standalone file)
|
|
350
|
+
"""
|
|
351
|
+
pass
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# ai_parrot/outputs/formats/charts/bokeh.py
|
|
2
|
+
from typing import Any, Optional, Tuple, Dict, List
|
|
3
|
+
import uuid
|
|
4
|
+
import json
|
|
5
|
+
from .chart import BaseChart
|
|
6
|
+
from . import register_renderer
|
|
7
|
+
from ...models.outputs import OutputMode
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
BOKEH_SYSTEM_PROMPT = """BOKEH CHART OUTPUT MODE:
|
|
11
|
+
Generate an interactive chart using Bokeh.
|
|
12
|
+
|
|
13
|
+
REQUIREMENTS:
|
|
14
|
+
1. Return Python code in a markdown code block (```python)
|
|
15
|
+
2. Use bokeh.plotting or bokeh.models
|
|
16
|
+
3. Store the plot in a variable named 'p' (recommended), 'plot', 'fig', or 'chart'
|
|
17
|
+
4. Make the chart self-contained with inline data
|
|
18
|
+
5. Use appropriate glyph types (circle, line, vbar, hbar, etc.)
|
|
19
|
+
6. Add titles, axis labels, and legends
|
|
20
|
+
7. Configure plot dimensions and tools
|
|
21
|
+
8. DO NOT call show() or save() - return code only
|
|
22
|
+
9. IMPORTANT: Use 'p' as the variable name for best compatibility
|
|
23
|
+
|
|
24
|
+
EXAMPLE:
|
|
25
|
+
```python
|
|
26
|
+
from bokeh.plotting import figure
|
|
27
|
+
from bokeh.models import HoverTool
|
|
28
|
+
|
|
29
|
+
x = ['A', 'B', 'C', 'D']
|
|
30
|
+
y = [23, 45, 12, 67]
|
|
31
|
+
|
|
32
|
+
p = figure(
|
|
33
|
+
x_range=x,
|
|
34
|
+
title="Sales by Category",
|
|
35
|
+
width=800,
|
|
36
|
+
height=400,
|
|
37
|
+
toolbar_location="above"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
p.vbar(x=x, top=y, width=0.8, color='navy', alpha=0.8)
|
|
41
|
+
|
|
42
|
+
p.xaxis.axis_label = "Category"
|
|
43
|
+
p.yaxis.axis_label = "Sales"
|
|
44
|
+
|
|
45
|
+
hover = HoverTool(tooltips=[("Category", "@x"), ("Sales", "@top")])
|
|
46
|
+
p.add_tools(hover)
|
|
47
|
+
```
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@register_renderer(OutputMode.BOKEH, system_prompt=BOKEH_SYSTEM_PROMPT)
|
|
52
|
+
class BokehRenderer(BaseChart):
|
|
53
|
+
"""Renderer for Bokeh charts"""
|
|
54
|
+
|
|
55
|
+
def execute_code(
|
|
56
|
+
self,
|
|
57
|
+
code: str,
|
|
58
|
+
pandas_tool: Any = None,
|
|
59
|
+
execution_state: Optional[Dict[str, Any]] = None,
|
|
60
|
+
**kwargs,
|
|
61
|
+
) -> Tuple[Any, Optional[str]]:
|
|
62
|
+
"""Execute Bokeh code using the shared Python execution context."""
|
|
63
|
+
extra_namespace = None
|
|
64
|
+
if pandas_tool is None:
|
|
65
|
+
from bokeh.plotting import figure as bokeh_figure
|
|
66
|
+
from bokeh import models, plotting
|
|
67
|
+
extra_namespace = {
|
|
68
|
+
'figure': bokeh_figure,
|
|
69
|
+
'models': models,
|
|
70
|
+
'plotting': plotting,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Execute using BaseRenderer logic
|
|
74
|
+
context, error = super().execute_code(
|
|
75
|
+
code,
|
|
76
|
+
pandas_tool=pandas_tool,
|
|
77
|
+
execution_state=execution_state,
|
|
78
|
+
extra_namespace=extra_namespace,
|
|
79
|
+
**kwargs,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if error:
|
|
83
|
+
return None, error
|
|
84
|
+
|
|
85
|
+
if not context:
|
|
86
|
+
return None, "Execution context was empty"
|
|
87
|
+
|
|
88
|
+
# Find all chart objects
|
|
89
|
+
if charts := self._find_chart_objects(context):
|
|
90
|
+
return charts, None
|
|
91
|
+
|
|
92
|
+
return None, "Code must define a plot variable (p, plot, fig, or chart)"
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _find_chart_objects(context: Dict[str, Any]) -> List[Any]:
|
|
96
|
+
"""Locate all Bokeh plot objects in the local namespace."""
|
|
97
|
+
from bokeh.embed import json_item
|
|
98
|
+
|
|
99
|
+
charts: List[Any] = []
|
|
100
|
+
seen_ids = set()
|
|
101
|
+
|
|
102
|
+
def is_bokeh_plot(obj: Any) -> bool:
|
|
103
|
+
"""Check if object is a Bokeh plot/figure."""
|
|
104
|
+
if obj is None:
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
# Skip renderer / BaseChart instances
|
|
108
|
+
if isinstance(obj, BaseChart):
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
# Check for Bokeh plot attributes
|
|
112
|
+
bokeh_attrs = ['renderers', 'toolbar', 'xaxis', 'yaxis']
|
|
113
|
+
has_attrs = all(hasattr(obj, attr) for attr in bokeh_attrs)
|
|
114
|
+
|
|
115
|
+
if has_attrs:
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
# Check by class name
|
|
119
|
+
class_name = obj.__class__.__name__
|
|
120
|
+
bokeh_classes = ['Figure', 'Plot', 'GridPlot']
|
|
121
|
+
if any(bc in class_name for bc in bokeh_classes):
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
# Check module
|
|
125
|
+
module = getattr(obj.__class__, '__module__', '')
|
|
126
|
+
return 'bokeh' in module
|
|
127
|
+
|
|
128
|
+
def is_serializable(obj: Any) -> bool:
|
|
129
|
+
"""Check if the Bokeh object can actually be serialized."""
|
|
130
|
+
try:
|
|
131
|
+
json_item(obj)
|
|
132
|
+
return True
|
|
133
|
+
except Exception:
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
def add_chart(obj: Any) -> None:
|
|
137
|
+
if is_bokeh_plot(obj) and id(obj) not in seen_ids:
|
|
138
|
+
# Only add if it can actually be serialized
|
|
139
|
+
if is_serializable(obj):
|
|
140
|
+
charts.append(obj)
|
|
141
|
+
seen_ids.add(id(obj))
|
|
142
|
+
else:
|
|
143
|
+
# Debug: log filtered objects (optional)
|
|
144
|
+
# print(f"Filtered non-serializable Bokeh object: {type(obj).__name__}")
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
# 1. Priority search for common variable names to preserve order
|
|
148
|
+
priority_vars = ['p', 'plot', 'fig', 'figure', 'chart']
|
|
149
|
+
for var_name in priority_vars:
|
|
150
|
+
if var_name in context:
|
|
151
|
+
add_chart(context[var_name])
|
|
152
|
+
|
|
153
|
+
# 2. Scan all locals for other chart objects
|
|
154
|
+
for var_name, obj in context.items():
|
|
155
|
+
if var_name.startswith('_') or var_name in priority_vars:
|
|
156
|
+
continue
|
|
157
|
+
add_chart(obj)
|
|
158
|
+
|
|
159
|
+
return charts
|
|
160
|
+
|
|
161
|
+
def _render_chart_content(self, chart_objs: Any, **kwargs) -> str:
|
|
162
|
+
"""
|
|
163
|
+
Render Bokeh-specific chart content (HTML/JS).
|
|
164
|
+
Handles a single chart or a list of charts.
|
|
165
|
+
"""
|
|
166
|
+
from bokeh.embed import json_item
|
|
167
|
+
|
|
168
|
+
# Ensure we have a list
|
|
169
|
+
charts = chart_objs if isinstance(chart_objs, list) else [chart_objs]
|
|
170
|
+
|
|
171
|
+
html_parts = []
|
|
172
|
+
|
|
173
|
+
for i, chart_obj in enumerate(charts):
|
|
174
|
+
print('CHART > ', chart_obj, type(chart_obj))
|
|
175
|
+
chart_id = f"bokeh-chart-{uuid.uuid4().hex[:8]}"
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
# Get Bokeh JSON item
|
|
179
|
+
item = json_item(chart_obj)
|
|
180
|
+
item_json = json.dumps(item)
|
|
181
|
+
except Exception as e:
|
|
182
|
+
chart_html = f'''
|
|
183
|
+
<div class="bokeh-chart-wrapper" style="margin-bottom: 20px;">
|
|
184
|
+
<div class="error-container">
|
|
185
|
+
<h3>⚠️ Bokeh Serialization Error</h3>
|
|
186
|
+
<p>{str(e)}</p>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
'''
|
|
190
|
+
html_parts.append(chart_html)
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
chart_html = f'''
|
|
194
|
+
<div class="bokeh-chart-wrapper" style="margin-bottom: 20px;">
|
|
195
|
+
<div id="{chart_id}" style="width: 100%;"></div>
|
|
196
|
+
<script type="text/javascript">
|
|
197
|
+
(function() {{
|
|
198
|
+
var item = {item_json};
|
|
199
|
+
|
|
200
|
+
if (typeof Bokeh === 'undefined') {{
|
|
201
|
+
console.error("Bokeh library not loaded");
|
|
202
|
+
document.getElementById('{chart_id}').innerHTML = "Error: Bokeh library not loaded.";
|
|
203
|
+
return;
|
|
204
|
+
}}
|
|
205
|
+
|
|
206
|
+
try {{
|
|
207
|
+
Bokeh.embed.embed_item(item, "{chart_id}");
|
|
208
|
+
console.log('Bokeh chart {chart_id} rendered successfully');
|
|
209
|
+
}} catch (error) {{
|
|
210
|
+
console.error('Error rendering Bokeh chart:', error);
|
|
211
|
+
document.getElementById('{chart_id}').innerHTML =
|
|
212
|
+
'<div style="color:red; padding:10px;">⚠️ Chart Rendering Error: ' + error.message + '</div>';
|
|
213
|
+
}}
|
|
214
|
+
}})();
|
|
215
|
+
</script>
|
|
216
|
+
</div>
|
|
217
|
+
'''
|
|
218
|
+
html_parts.append(chart_html)
|
|
219
|
+
|
|
220
|
+
return "\n".join(html_parts)
|
|
221
|
+
|
|
222
|
+
def to_html(
|
|
223
|
+
self,
|
|
224
|
+
chart_obj: Any,
|
|
225
|
+
mode: str = 'partial',
|
|
226
|
+
**kwargs
|
|
227
|
+
) -> str:
|
|
228
|
+
"""
|
|
229
|
+
Convert Bokeh chart(s) to HTML.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
chart_obj: Bokeh plot object or list of plot objects
|
|
233
|
+
mode: 'partial' or 'complete'
|
|
234
|
+
**kwargs: Additional parameters
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
HTML string
|
|
238
|
+
"""
|
|
239
|
+
# Bokeh libraries for <head>
|
|
240
|
+
try:
|
|
241
|
+
from bokeh import __version__ as bokeh_version
|
|
242
|
+
except:
|
|
243
|
+
bokeh_version = "3.3.0" # fallback version
|
|
244
|
+
|
|
245
|
+
extra_head = f'''
|
|
246
|
+
<!-- Bokeh -->
|
|
247
|
+
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-{bokeh_version}.min.js"></script>
|
|
248
|
+
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-{bokeh_version}.min.js"></script>
|
|
249
|
+
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-{bokeh_version}.min.js"></script>
|
|
250
|
+
'''
|
|
251
|
+
|
|
252
|
+
kwargs['extra_head'] = kwargs.get('extra_head', extra_head)
|
|
253
|
+
|
|
254
|
+
# Call parent to_html (which calls _render_chart_content)
|
|
255
|
+
return super().to_html(chart_obj, mode=mode, **kwargs)
|
|
256
|
+
|
|
257
|
+
def to_json(self, chart_obj: Any) -> Optional[Any]:
|
|
258
|
+
"""Export Bokeh JSON specification (returns list if multiple)."""
|
|
259
|
+
from bokeh.embed import json_item
|
|
260
|
+
|
|
261
|
+
charts = chart_obj if isinstance(chart_obj, list) else [chart_obj]
|
|
262
|
+
results = []
|
|
263
|
+
|
|
264
|
+
for chart in charts:
|
|
265
|
+
try:
|
|
266
|
+
item = json_item(chart)
|
|
267
|
+
results.append(json.loads(json.dumps(item)))
|
|
268
|
+
except Exception as e:
|
|
269
|
+
results.append({'error': str(e)})
|
|
270
|
+
|
|
271
|
+
return results if len(results) > 1 else results[0] if results else None
|
|
272
|
+
|
|
273
|
+
async def render(
|
|
274
|
+
self,
|
|
275
|
+
response: Any,
|
|
276
|
+
theme: str = 'monokai',
|
|
277
|
+
environment: str = 'html',
|
|
278
|
+
include_code: bool = False,
|
|
279
|
+
html_mode: str = 'partial',
|
|
280
|
+
**kwargs
|
|
281
|
+
) -> Tuple[Any, Optional[Any]]:
|
|
282
|
+
"""Render Bokeh chart(s)."""
|
|
283
|
+
|
|
284
|
+
# 1. Extract Code
|
|
285
|
+
code = getattr(response, 'code', None)
|
|
286
|
+
output_format = kwargs.get('output_format', environment)
|
|
287
|
+
|
|
288
|
+
# Fallback to extracting from text content
|
|
289
|
+
if not code:
|
|
290
|
+
content = self._get_content(response)
|
|
291
|
+
code = self._extract_code(content)
|
|
292
|
+
|
|
293
|
+
if not code:
|
|
294
|
+
error_msg = "No chart code found in response"
|
|
295
|
+
if output_format == 'terminal':
|
|
296
|
+
return error_msg, None
|
|
297
|
+
return self._wrap_for_environment(
|
|
298
|
+
f"<div class='error'>{error_msg}</div>",
|
|
299
|
+
output_format
|
|
300
|
+
), None
|
|
301
|
+
|
|
302
|
+
# 2. Execute Code
|
|
303
|
+
chart_objs, error = self.execute_code(
|
|
304
|
+
code,
|
|
305
|
+
pandas_tool=kwargs.pop('pandas_tool', None),
|
|
306
|
+
execution_state=kwargs.pop('execution_state', None),
|
|
307
|
+
**kwargs,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
if error:
|
|
311
|
+
if output_format == 'terminal':
|
|
312
|
+
return f"Error generating chart: {error}", None
|
|
313
|
+
return self._wrap_for_environment(
|
|
314
|
+
self._render_error(error, code, theme),
|
|
315
|
+
output_format
|
|
316
|
+
), None
|
|
317
|
+
|
|
318
|
+
# 3. Handle Jupyter/Notebook Environment
|
|
319
|
+
if output_format in {'jupyter', 'notebook', 'ipython', 'colab'}:
|
|
320
|
+
from bokeh.embed import components
|
|
321
|
+
|
|
322
|
+
charts = chart_objs if isinstance(chart_objs, list) else [chart_objs]
|
|
323
|
+
|
|
324
|
+
if len(charts) == 1:
|
|
325
|
+
# Single chart
|
|
326
|
+
script, div = components(charts[0])
|
|
327
|
+
return code, f"{script}{div}"
|
|
328
|
+
else:
|
|
329
|
+
# Multiple charts
|
|
330
|
+
script, divs = components(charts)
|
|
331
|
+
combined = script + "".join(divs)
|
|
332
|
+
return code, combined
|
|
333
|
+
|
|
334
|
+
# 4. Generate HTML for Web/Terminal
|
|
335
|
+
html_output = self.to_html(
|
|
336
|
+
chart_objs,
|
|
337
|
+
mode=html_mode,
|
|
338
|
+
include_code=include_code,
|
|
339
|
+
code=code,
|
|
340
|
+
theme=theme,
|
|
341
|
+
title=kwargs.get('title', 'Bokeh Chart'),
|
|
342
|
+
icon='📊',
|
|
343
|
+
**kwargs
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
# 5. Return based on output format
|
|
347
|
+
if output_format == 'html':
|
|
348
|
+
return code, html_output
|
|
349
|
+
elif output_format == 'json':
|
|
350
|
+
return code, self.to_json(chart_objs)
|
|
351
|
+
elif output_format == 'terminal':
|
|
352
|
+
# For terminal, return the code and HTML
|
|
353
|
+
return code, html_output
|
|
354
|
+
|
|
355
|
+
# Default behavior: Return code as content, HTML as wrapped
|
|
356
|
+
return code, html_output
|