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,436 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
from abc import abstractmethod
|
|
3
|
+
import re
|
|
4
|
+
import html
|
|
5
|
+
import uuid
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from pygments import highlight
|
|
8
|
+
from pygments.lexers.python import PythonLexer
|
|
9
|
+
from pygments.formatters.html import HtmlFormatter
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from ipywidgets import HTML as IPyHTML
|
|
13
|
+
IPYWIDGETS_AVAILABLE = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
IPYWIDGETS_AVAILABLE = False
|
|
16
|
+
|
|
17
|
+
from .base import BaseRenderer
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseChart(BaseRenderer):
|
|
21
|
+
"""Base class for chart renderers - extends BaseRenderer with chart-specific methods"""
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def _extract_code(content: str) -> Optional[str]:
|
|
25
|
+
"""Extract Python code from markdown blocks."""
|
|
26
|
+
pattern = r'```(?:python)?\n(.*?)```'
|
|
27
|
+
matches = re.findall(pattern, content, re.DOTALL)
|
|
28
|
+
return matches[0].strip() if matches else None
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def _highlight_code(code: str, theme: str = 'monokai') -> str:
|
|
32
|
+
"""Apply syntax highlighting to code."""
|
|
33
|
+
try:
|
|
34
|
+
formatter = HtmlFormatter(style=theme, noclasses=True, cssclass='code')
|
|
35
|
+
return highlight(code, PythonLexer(), formatter)
|
|
36
|
+
except ImportError:
|
|
37
|
+
escaped = html.escape(code)
|
|
38
|
+
return f'<pre class="code"><code>{escaped}</code></pre>'
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def _wrap_for_environment(content: Any, environment: str) -> Any:
|
|
42
|
+
"""Wrap content based on environment."""
|
|
43
|
+
if isinstance(content, str) and environment in {'jupyter', 'ipython', 'colab'} and IPYWIDGETS_AVAILABLE:
|
|
44
|
+
return IPyHTML(value=content)
|
|
45
|
+
return content
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def _build_code_section(code: str, theme: str, icon: str = "📊") -> str:
|
|
49
|
+
"""Build collapsible code section."""
|
|
50
|
+
highlighted = BaseChart._highlight_code(code, theme)
|
|
51
|
+
return f'''
|
|
52
|
+
<details class="ap-code-accordion">
|
|
53
|
+
<summary class="ap-code-header">
|
|
54
|
+
<span>{icon} View Code</span>
|
|
55
|
+
<span class="ap-toggle-icon">▶</span>
|
|
56
|
+
</summary>
|
|
57
|
+
<div class="ap-code-content">
|
|
58
|
+
{highlighted}
|
|
59
|
+
</div>
|
|
60
|
+
</details>
|
|
61
|
+
'''
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def _render_error(error: str, code: str, theme: str) -> str:
|
|
65
|
+
"""Render error message with code."""
|
|
66
|
+
highlighted = BaseChart._highlight_code(code, theme)
|
|
67
|
+
return f'''
|
|
68
|
+
{BaseChart._get_chart_styles()}
|
|
69
|
+
<div class="ap-error-container">
|
|
70
|
+
<h3>⚠️ Chart Generation Error</h3>
|
|
71
|
+
<p class="ap-error-message">{error}</p>
|
|
72
|
+
<details class="ap-code-accordion" open>
|
|
73
|
+
<summary class="ap-code-header">Code with Error</summary>
|
|
74
|
+
<div class="ap-code-content">{highlighted}</div>
|
|
75
|
+
</details>
|
|
76
|
+
</div>
|
|
77
|
+
'''
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def _get_chart_styles() -> str:
|
|
81
|
+
"""CSS styles specific to charts."""
|
|
82
|
+
return '''
|
|
83
|
+
<style>
|
|
84
|
+
.ap-chart-container {
|
|
85
|
+
background: white;
|
|
86
|
+
border-radius: 8px;
|
|
87
|
+
}
|
|
88
|
+
.ap-chart-wrapper {
|
|
89
|
+
min-height: 400px;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
align-items: center;
|
|
92
|
+
}
|
|
93
|
+
.ap-chart-guidance {
|
|
94
|
+
background: #f0f4ff;
|
|
95
|
+
border-left: 4px solid #667eea;
|
|
96
|
+
padding: 16px 20px;
|
|
97
|
+
border-radius: 6px;
|
|
98
|
+
}
|
|
99
|
+
.ap-chart-guidance h3 {
|
|
100
|
+
font-size: 16px;
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
color: #364152;
|
|
103
|
+
}
|
|
104
|
+
.ap-chart-guidance ol {
|
|
105
|
+
margin: 0 0 0 20px;
|
|
106
|
+
padding: 0;
|
|
107
|
+
}
|
|
108
|
+
.ap-chart-guidance li {
|
|
109
|
+
margin-bottom: 6px;
|
|
110
|
+
line-height: 1.4;
|
|
111
|
+
}
|
|
112
|
+
.ap-chart-note {
|
|
113
|
+
background: #fffaf0;
|
|
114
|
+
border-left: 4px solid #f6ad55;
|
|
115
|
+
padding: 2px 4px;
|
|
116
|
+
border-radius: 6px;
|
|
117
|
+
margin-bottom: 2px;
|
|
118
|
+
color: #744210;
|
|
119
|
+
font-size: 14px;
|
|
120
|
+
}
|
|
121
|
+
.ap-code-accordion {
|
|
122
|
+
margin-top: 20px;
|
|
123
|
+
border: 1px solid #e0e0e0;
|
|
124
|
+
border-radius: 6px;
|
|
125
|
+
overflow: hidden;
|
|
126
|
+
}
|
|
127
|
+
.ap-code-header {
|
|
128
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
129
|
+
color: white;
|
|
130
|
+
padding: 12px 20px;
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
display: flex;
|
|
133
|
+
justify-content: space-between;
|
|
134
|
+
align-items: center;
|
|
135
|
+
font-weight: 600;
|
|
136
|
+
user-select: none;
|
|
137
|
+
}
|
|
138
|
+
.ap-code-header:hover {
|
|
139
|
+
background: linear-gradient(135deg, #5568d3 0%, #653a8e 100%);
|
|
140
|
+
}
|
|
141
|
+
.ap-toggle-icon {
|
|
142
|
+
transition: transform 0.3s ease;
|
|
143
|
+
}
|
|
144
|
+
details[open] .ap-toggle-icon {
|
|
145
|
+
transform: rotate(90deg);
|
|
146
|
+
}
|
|
147
|
+
.ap-code-content {
|
|
148
|
+
background: #272822;
|
|
149
|
+
padding: 15px;
|
|
150
|
+
overflow-x: auto;
|
|
151
|
+
}
|
|
152
|
+
.ap-code-content pre {
|
|
153
|
+
margin: 0;
|
|
154
|
+
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
|
155
|
+
font-size: 13px;
|
|
156
|
+
line-height: 1.5;
|
|
157
|
+
}
|
|
158
|
+
.ap-error-container {
|
|
159
|
+
background: #fff3cd;
|
|
160
|
+
border: 1px solid #ffc107;
|
|
161
|
+
border-radius: 8px;
|
|
162
|
+
padding: 20px;
|
|
163
|
+
margin: 20px 0;
|
|
164
|
+
}
|
|
165
|
+
.ap-error-message {
|
|
166
|
+
color: #856404;
|
|
167
|
+
font-weight: 500;
|
|
168
|
+
margin: 10px 0;
|
|
169
|
+
}
|
|
170
|
+
</style>
|
|
171
|
+
'''
|
|
172
|
+
|
|
173
|
+
def _save_to_disk(self, chart_obj: Any, filename: str = None) -> str:
|
|
174
|
+
"""Save chart to disk for terminal viewing."""
|
|
175
|
+
if not filename:
|
|
176
|
+
filename = f"chart_{uuid.uuid4().hex[:8]}.png"
|
|
177
|
+
|
|
178
|
+
# Ensure we have a directory
|
|
179
|
+
# TODO: using a fixed path for now; ideally this should be configurable
|
|
180
|
+
output_dir = Path("outputs/charts")
|
|
181
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
182
|
+
|
|
183
|
+
filepath = output_dir / filename
|
|
184
|
+
chart_obj.savefig(str(filepath), bbox_inches='tight', dpi=100)
|
|
185
|
+
return str(filepath)
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def _build_html_document(
|
|
189
|
+
chart_content: str,
|
|
190
|
+
code_section: str = '',
|
|
191
|
+
title: str = 'AI-Parrot Chart',
|
|
192
|
+
extra_head: str = '',
|
|
193
|
+
mode: str = 'partial'
|
|
194
|
+
) -> str:
|
|
195
|
+
"""Build HTML document wrapper for charts."""
|
|
196
|
+
if mode == 'partial':
|
|
197
|
+
return f'''
|
|
198
|
+
{BaseChart._get_chart_styles()}
|
|
199
|
+
<div class="ap-chart-container">
|
|
200
|
+
<div class="ap-chart-wrapper">
|
|
201
|
+
{chart_content}
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
{code_section}
|
|
205
|
+
'''
|
|
206
|
+
|
|
207
|
+
elif mode == 'complete':
|
|
208
|
+
# Generate unique ID for the container
|
|
209
|
+
container_id = f"container-{uuid.uuid4().hex[:8]}"
|
|
210
|
+
|
|
211
|
+
return f'''<!DOCTYPE html>
|
|
212
|
+
<html lang="en">
|
|
213
|
+
<head>
|
|
214
|
+
<meta charset="UTF-8">
|
|
215
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
216
|
+
<title>{title}</title>
|
|
217
|
+
|
|
218
|
+
{extra_head}
|
|
219
|
+
|
|
220
|
+
<style>
|
|
221
|
+
.ap-chart-wrapper {{
|
|
222
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
|
223
|
+
'Helvetica Neue', Arial, sans-serif;
|
|
224
|
+
padding: 5px;
|
|
225
|
+
line-height: 1.6;
|
|
226
|
+
box-sizing: border-box;
|
|
227
|
+
}}
|
|
228
|
+
|
|
229
|
+
.ap-chart-wrapper * {{
|
|
230
|
+
box-sizing: border-box;
|
|
231
|
+
}}
|
|
232
|
+
|
|
233
|
+
.ap-media-container {{
|
|
234
|
+
max-width: 1200px;
|
|
235
|
+
margin: 0 auto;
|
|
236
|
+
}}
|
|
237
|
+
|
|
238
|
+
.ap-chart-container {{
|
|
239
|
+
border-radius: 12px;
|
|
240
|
+
padding: 5px;
|
|
241
|
+
}}
|
|
242
|
+
|
|
243
|
+
.ap-chart-wrapper {{
|
|
244
|
+
min-height: 400px;
|
|
245
|
+
display: flex;
|
|
246
|
+
justify-content: center;
|
|
247
|
+
align-items: center;
|
|
248
|
+
}}
|
|
249
|
+
|
|
250
|
+
.ap-code-accordion {{
|
|
251
|
+
margin-top: 20px;
|
|
252
|
+
border: 1px solid #e0e0e0;
|
|
253
|
+
border-radius: 8px;
|
|
254
|
+
overflow: hidden;
|
|
255
|
+
background: white;
|
|
256
|
+
}}
|
|
257
|
+
|
|
258
|
+
.ap-code-header {{
|
|
259
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
260
|
+
color: white;
|
|
261
|
+
padding: 14px 20px;
|
|
262
|
+
cursor: pointer;
|
|
263
|
+
display: flex;
|
|
264
|
+
justify-content: space-between;
|
|
265
|
+
align-items: center;
|
|
266
|
+
font-weight: 600;
|
|
267
|
+
user-select: none;
|
|
268
|
+
transition: all 0.3s ease;
|
|
269
|
+
}}
|
|
270
|
+
|
|
271
|
+
.ap-code-header:hover {{
|
|
272
|
+
background: linear-gradient(135deg, #5568d3 0%, #653a8e 100%);
|
|
273
|
+
}}
|
|
274
|
+
|
|
275
|
+
.ap-toggle-icon {{
|
|
276
|
+
transition: transform 0.3s ease;
|
|
277
|
+
font-size: 12px;
|
|
278
|
+
}}
|
|
279
|
+
|
|
280
|
+
details[open] .ap-toggle-icon {{
|
|
281
|
+
transform: rotate(90deg);
|
|
282
|
+
}}
|
|
283
|
+
|
|
284
|
+
.ap-code-content {{
|
|
285
|
+
background: #272822;
|
|
286
|
+
padding: 20px;
|
|
287
|
+
overflow-x: auto;
|
|
288
|
+
}}
|
|
289
|
+
|
|
290
|
+
.ap-code-content pre {{
|
|
291
|
+
margin: 0;
|
|
292
|
+
font-family: 'Monaco', 'Menlo', 'Consolas', 'Courier New', monospace;
|
|
293
|
+
font-size: 14px;
|
|
294
|
+
line-height: 1.6;
|
|
295
|
+
}}
|
|
296
|
+
|
|
297
|
+
.ap-error-container {{
|
|
298
|
+
background: #fff3cd;
|
|
299
|
+
border: 2px solid #ffc107;
|
|
300
|
+
border-radius: 8px;
|
|
301
|
+
padding: 20px;
|
|
302
|
+
margin: 20px 0;
|
|
303
|
+
}}
|
|
304
|
+
|
|
305
|
+
.ap-error-container h3 {{
|
|
306
|
+
color: #856404;
|
|
307
|
+
margin-bottom: 10px;
|
|
308
|
+
}}
|
|
309
|
+
|
|
310
|
+
.ap-error-message {{
|
|
311
|
+
color: #856404;
|
|
312
|
+
font-weight: 500;
|
|
313
|
+
margin: 10px 0;
|
|
314
|
+
}}
|
|
315
|
+
|
|
316
|
+
@media (max-width: 768px) {{
|
|
317
|
+
.ap-chart-wrapper {{
|
|
318
|
+
padding: 1px;
|
|
319
|
+
}}
|
|
320
|
+
|
|
321
|
+
.ap-chart-container {{
|
|
322
|
+
padding: 2px;
|
|
323
|
+
}}
|
|
324
|
+
}}
|
|
325
|
+
</style>
|
|
326
|
+
</head>
|
|
327
|
+
<body>
|
|
328
|
+
<div class="ap-chart-wrapper">
|
|
329
|
+
<div class="ap-media-container" id="{container_id}">
|
|
330
|
+
<div class="ap-chart-container">
|
|
331
|
+
<div class="ap-chart-wrapper">
|
|
332
|
+
{chart_content}
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
{code_section}
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<script>
|
|
341
|
+
(function() {{
|
|
342
|
+
function sendSize() {{
|
|
343
|
+
const width = document.documentElement.scrollWidth || window.innerWidth;
|
|
344
|
+
const height = document.documentElement.scrollHeight || window.innerHeight;
|
|
345
|
+
|
|
346
|
+
parent.postMessage(
|
|
347
|
+
{{
|
|
348
|
+
type: "iframe_resize",
|
|
349
|
+
width: width,
|
|
350
|
+
height: height,
|
|
351
|
+
containerId: "{container_id}"
|
|
352
|
+
}},
|
|
353
|
+
"*"
|
|
354
|
+
);
|
|
355
|
+
}}
|
|
356
|
+
|
|
357
|
+
// Send initial size
|
|
358
|
+
sendSize();
|
|
359
|
+
|
|
360
|
+
// Detect viewport resize
|
|
361
|
+
window.addEventListener("resize", sendSize);
|
|
362
|
+
|
|
363
|
+
// Detect content changes (e.g., height changes from DOM mutations)
|
|
364
|
+
const observer = new ResizeObserver(sendSize);
|
|
365
|
+
observer.observe(document.body);
|
|
366
|
+
|
|
367
|
+
// Also observe the container for more precise tracking
|
|
368
|
+
const container = document.getElementById("{container_id}");
|
|
369
|
+
if (container) {{
|
|
370
|
+
observer.observe(container);
|
|
371
|
+
}}
|
|
372
|
+
}})();
|
|
373
|
+
</script>
|
|
374
|
+
</body>
|
|
375
|
+
</html>'''
|
|
376
|
+
|
|
377
|
+
else:
|
|
378
|
+
raise ValueError(f"Invalid mode: {mode}. Must be 'partial' or 'complete'")
|
|
379
|
+
|
|
380
|
+
@abstractmethod
|
|
381
|
+
def _render_chart_content(self, chart_obj: Any, **kwargs) -> str:
|
|
382
|
+
"""
|
|
383
|
+
Render the chart-specific content (to be embedded in HTML).
|
|
384
|
+
This should return just the chart div/script, not the full HTML document.
|
|
385
|
+
|
|
386
|
+
Each chart renderer must implement this method to generate their
|
|
387
|
+
specific chart content (Altair vega-embed, Plotly div, etc.)
|
|
388
|
+
"""
|
|
389
|
+
pass
|
|
390
|
+
|
|
391
|
+
def to_html(
|
|
392
|
+
self,
|
|
393
|
+
chart_obj: Any,
|
|
394
|
+
mode: str = 'partial',
|
|
395
|
+
include_code: bool = False,
|
|
396
|
+
code: Optional[str] = None,
|
|
397
|
+
theme: str = 'monokai',
|
|
398
|
+
title: str = 'AI-Parrot Chart',
|
|
399
|
+
**kwargs
|
|
400
|
+
) -> str:
|
|
401
|
+
"""
|
|
402
|
+
Convert chart object to HTML.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
chart_obj: Chart object to render
|
|
406
|
+
mode: 'partial' for embeddable HTML or 'complete' for full document
|
|
407
|
+
include_code: Whether to include code section
|
|
408
|
+
code: Python code to display
|
|
409
|
+
theme: Code highlighting theme
|
|
410
|
+
title: Document title (for complete mode)
|
|
411
|
+
**kwargs: Additional parameters passed to _render_chart_content
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
HTML string based on mode
|
|
415
|
+
"""
|
|
416
|
+
# Get chart-specific content from subclass
|
|
417
|
+
chart_content = self._render_chart_content(chart_obj, **kwargs)
|
|
418
|
+
|
|
419
|
+
# Build code section if requested
|
|
420
|
+
code_section = ''
|
|
421
|
+
if include_code and code:
|
|
422
|
+
code_section = self._build_code_section(
|
|
423
|
+
code, theme, kwargs.get('icon', '📊')
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
# Get extra head content if provided by subclass
|
|
427
|
+
extra_head = kwargs.get('extra_head', '')
|
|
428
|
+
|
|
429
|
+
# Build final HTML
|
|
430
|
+
return self._build_html_document(
|
|
431
|
+
chart_content=chart_content,
|
|
432
|
+
code_section=code_section,
|
|
433
|
+
title=title,
|
|
434
|
+
extra_head=extra_head,
|
|
435
|
+
mode=mode
|
|
436
|
+
)
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# ai_parrot/outputs/formats/charts/d3.py
|
|
2
|
+
import contextlib
|
|
3
|
+
from typing import Any, Optional, Tuple, Dict, List
|
|
4
|
+
import json
|
|
5
|
+
import html
|
|
6
|
+
import re
|
|
7
|
+
import uuid
|
|
8
|
+
from .base import BaseChart
|
|
9
|
+
from . import register_renderer
|
|
10
|
+
from ...models.outputs import OutputMode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
D3_SYSTEM_PROMPT = """🚨 CRITICAL – OUTPUT FORMAT IS STRICT 🚨
|
|
14
|
+
|
|
15
|
+
You are generating **text only** — specifically a single fenced ```json block that contains:
|
|
16
|
+
- "teaching_steps": array of 3–6 short bullets.
|
|
17
|
+
- "javascript": string with **D3 code only** (newlines escaped as \\n).
|
|
18
|
+
- Optional: "notes": short string.
|
|
19
|
+
|
|
20
|
+
ABSOLUTE RULES
|
|
21
|
+
1) Do **NOT** use any fence other than ```json. Never use ```javascript.
|
|
22
|
+
2) The "javascript" must target **'#chart'** and assume **global d3** (no imports/exports).
|
|
23
|
+
3) **No HTML/CSS**: no <!DOCTYPE>, <html>, <head>, <body>, <script>, <style>, <link>.
|
|
24
|
+
4) Do not include external scripts, ESM imports, require(), module, etc.
|
|
25
|
+
5) If you think HTML is required: **STOP** and instead return the JSON with only D3 code.
|
|
26
|
+
|
|
27
|
+
🚫 If your output contains any of these substrings, it will be rejected:
|
|
28
|
+
"<!DOCTYPE", "<html", "<head", "<body", "<script", "<link", "<style", "import ", "export ", "require("
|
|
29
|
+
|
|
30
|
+
✅ EXAMPLE (illustrative):
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"teaching_steps": [
|
|
34
|
+
"Append an SVG into #chart with margins.",
|
|
35
|
+
"Create time and linear scales.",
|
|
36
|
+
"Render axes and a line path."
|
|
37
|
+
],
|
|
38
|
+
"javascript": "const margin={top:20,right:20,bottom:30,left:40};\\nconst width=640,height=400;\\nconst svg=d3.select('#chart').append('svg').attr('width',width).attr('height',height);\\n/* more d3 code here */",
|
|
39
|
+
"notes": "Optional clarifications."
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
Remember:
|
|
43
|
+
* Container is always '#chart'.
|
|
44
|
+
* Use the global d3 (no imports).
|
|
45
|
+
* Provide complete, runnable D3 code only (no HTML or script tags).
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@register_renderer(OutputMode.D3, system_prompt=D3_SYSTEM_PROMPT)
|
|
50
|
+
class D3Renderer(BaseChart):
|
|
51
|
+
"""Renderer for D3.js visualizations"""
|
|
52
|
+
|
|
53
|
+
def execute_code(
|
|
54
|
+
self,
|
|
55
|
+
code: str,
|
|
56
|
+
pandas_tool: "PythonPandasTool | None" = None,
|
|
57
|
+
execution_state: Optional[Dict[str, Any]] = None,
|
|
58
|
+
**kwargs,
|
|
59
|
+
) -> Tuple[Any, Optional[str]]:
|
|
60
|
+
"""
|
|
61
|
+
For D3, we don't execute JavaScript - just validate and return it.
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
# Basic validation - check if it looks like D3 code
|
|
65
|
+
if 'd3.' not in code and 'D3' not in code:
|
|
66
|
+
return None, "Code doesn't appear to use D3.js (no d3. references found)"
|
|
67
|
+
|
|
68
|
+
# Return the code itself as the "chart object"
|
|
69
|
+
return code, None
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
return None, f"Validation error: {str(e)}"
|
|
73
|
+
|
|
74
|
+
def _render_chart_content(
|
|
75
|
+
self,
|
|
76
|
+
chart_obj: Any,
|
|
77
|
+
teaching_steps: Optional[List[str]] = None,
|
|
78
|
+
notes: Optional[str] = None,
|
|
79
|
+
**kwargs
|
|
80
|
+
) -> str:
|
|
81
|
+
"""Render D3 visualization content."""
|
|
82
|
+
# chart_obj is the JavaScript code
|
|
83
|
+
js_code = chart_obj
|
|
84
|
+
chart_id = f"d3-chart-{uuid.uuid4().hex[:8]}"
|
|
85
|
+
|
|
86
|
+
# Replace '#chart' with our specific chart ID
|
|
87
|
+
js_code = js_code.replace("'#chart'", f"'#{chart_id}'")
|
|
88
|
+
js_code = js_code.replace('"#chart"', f'"#{chart_id}"')
|
|
89
|
+
js_code = js_code.replace('`#chart`', f'`#{chart_id}`')
|
|
90
|
+
|
|
91
|
+
guidance_sections: List[str] = []
|
|
92
|
+
if teaching_steps:
|
|
93
|
+
items = ''.join(f"<li>{html.escape(step)}</li>" for step in teaching_steps)
|
|
94
|
+
guidance_sections.append(
|
|
95
|
+
f"""
|
|
96
|
+
<div class=\"ap-chart-guidance\">
|
|
97
|
+
<h3>How this chart works</h3>
|
|
98
|
+
<ol>{items}</ol>
|
|
99
|
+
</div>
|
|
100
|
+
"""
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if notes:
|
|
104
|
+
guidance_sections.append(
|
|
105
|
+
f"""
|
|
106
|
+
<div class=\"ap-chart-note\"><strong>Notes:</strong> {html.escape(notes)}</div>
|
|
107
|
+
"""
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
guidance_html = '\n'.join(guidance_sections)
|
|
111
|
+
|
|
112
|
+
return f'''
|
|
113
|
+
{guidance_html}
|
|
114
|
+
<div id="{chart_id}" style="width: 100%; min-height: 400px;"></div>
|
|
115
|
+
<script type="text/javascript">
|
|
116
|
+
(function() {{
|
|
117
|
+
{js_code}
|
|
118
|
+
}})();
|
|
119
|
+
</script>
|
|
120
|
+
'''
|
|
121
|
+
|
|
122
|
+
def to_html(
|
|
123
|
+
self,
|
|
124
|
+
chart_obj: Any,
|
|
125
|
+
mode: str = 'partial',
|
|
126
|
+
**kwargs
|
|
127
|
+
) -> str:
|
|
128
|
+
"""Convert D3 visualization to HTML."""
|
|
129
|
+
# D3.js library for <head>
|
|
130
|
+
d3_version = kwargs.get('d3_version', '7')
|
|
131
|
+
extra_head = f'''
|
|
132
|
+
<!-- D3.js -->
|
|
133
|
+
<script src="https://d3js.org/d3.v{d3_version}.min.js"></script>
|
|
134
|
+
'''
|
|
135
|
+
|
|
136
|
+
kwargs['extra_head'] = extra_head
|
|
137
|
+
|
|
138
|
+
# Call parent to_html
|
|
139
|
+
return super().to_html(chart_obj, mode=mode, **kwargs)
|
|
140
|
+
|
|
141
|
+
def to_json(self, chart_obj: Any) -> Optional[Dict]:
|
|
142
|
+
"""D3 code doesn't have a JSON representation."""
|
|
143
|
+
return {
|
|
144
|
+
'type': 'd3_visualization',
|
|
145
|
+
'code_length': len(chart_obj),
|
|
146
|
+
'note': 'D3 visualizations are JavaScript code, not JSON data'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async def render(
|
|
150
|
+
self,
|
|
151
|
+
response: Any,
|
|
152
|
+
theme: str = 'monokai',
|
|
153
|
+
environment: str = 'terminal',
|
|
154
|
+
export_format: str = 'html',
|
|
155
|
+
include_code: bool = False,
|
|
156
|
+
html_mode: str = 'partial',
|
|
157
|
+
**kwargs
|
|
158
|
+
) -> Tuple[Any, Optional[Any]]:
|
|
159
|
+
"""Render D3 visualization."""
|
|
160
|
+
content = self._get_content(response)
|
|
161
|
+
|
|
162
|
+
# Extract payload containing instructions and JavaScript code
|
|
163
|
+
payload = self._extract_payload(content)
|
|
164
|
+
|
|
165
|
+
if not payload:
|
|
166
|
+
error_msg = (
|
|
167
|
+
"No D3 payload found in response (expected ```json block with a \"javascript\" field)"
|
|
168
|
+
)
|
|
169
|
+
error_html = "<div class='error'>Missing D3 JSON payload in response</div>"
|
|
170
|
+
return error_msg, error_html
|
|
171
|
+
|
|
172
|
+
code = payload['javascript']
|
|
173
|
+
teaching_steps = payload.get('teaching_steps', [])
|
|
174
|
+
notes = payload.get('notes')
|
|
175
|
+
|
|
176
|
+
# "Execute" (validate) code
|
|
177
|
+
js_code, error = self.execute_code(code)
|
|
178
|
+
|
|
179
|
+
if error:
|
|
180
|
+
error_html = self._render_error(error, code, theme)
|
|
181
|
+
return code, error_html
|
|
182
|
+
|
|
183
|
+
# Generate HTML
|
|
184
|
+
render_kwargs = dict(kwargs)
|
|
185
|
+
title = render_kwargs.pop('title', 'D3 Visualization')
|
|
186
|
+
render_kwargs['teaching_steps'] = teaching_steps
|
|
187
|
+
if notes:
|
|
188
|
+
render_kwargs['notes'] = notes
|
|
189
|
+
|
|
190
|
+
html_output = self.to_html(
|
|
191
|
+
js_code,
|
|
192
|
+
mode=html_mode,
|
|
193
|
+
include_code=include_code,
|
|
194
|
+
code=code,
|
|
195
|
+
theme=theme,
|
|
196
|
+
title=title,
|
|
197
|
+
icon='📊',
|
|
198
|
+
**render_kwargs
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Return (code, html)
|
|
202
|
+
return code, html_output
|
|
203
|
+
|
|
204
|
+
@staticmethod
|
|
205
|
+
def _extract_payload(content: str) -> Optional[Dict[str, Any]]:
|
|
206
|
+
"""Extract a D3 payload from raw JSON, ```json fences, ```javascript fences, or HTML <script> tags."""
|
|
207
|
+
# 0) Fast path: raw JSON body (no fences)
|
|
208
|
+
with contextlib.suppress(Exception):
|
|
209
|
+
raw = json.loads(content)
|
|
210
|
+
if isinstance(raw, dict):
|
|
211
|
+
js = raw.get("javascript") or raw.get("code")
|
|
212
|
+
if isinstance(js, str) and js.strip():
|
|
213
|
+
steps = raw.get("teaching_steps") or []
|
|
214
|
+
notes = raw.get("notes")
|
|
215
|
+
return {
|
|
216
|
+
"javascript": js.strip(),
|
|
217
|
+
"teaching_steps": [str(s).strip() for s in steps if str(s).strip()] if isinstance(steps, list) else [],
|
|
218
|
+
"notes": notes.strip() if isinstance(notes, str) else None,
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
# 1) Markdown ```json fences (case-insensitive)
|
|
222
|
+
for block in re.findall(r'```json\s*(.*?)```', content, re.DOTALL | re.IGNORECASE):
|
|
223
|
+
try:
|
|
224
|
+
obj = json.loads(block)
|
|
225
|
+
except Exception:
|
|
226
|
+
continue
|
|
227
|
+
if isinstance(obj, dict):
|
|
228
|
+
js = obj.get("javascript") or obj.get("code")
|
|
229
|
+
if isinstance(js, str) and js.strip():
|
|
230
|
+
steps = obj.get("teaching_steps") or []
|
|
231
|
+
notes = obj.get("notes")
|
|
232
|
+
return {
|
|
233
|
+
"javascript": js.strip(),
|
|
234
|
+
"teaching_steps": [str(s).strip() for s in steps if str(s).strip()] if isinstance(steps, list) else [],
|
|
235
|
+
"notes": notes.strip() if isinstance(notes, str) else None,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
# 2) Markdown ```javascript / ```js fences → salvage as JS
|
|
239
|
+
for js in re.findall(r'```(?:javascript|js)\s*(.*?)```', content, re.DOTALL | re.IGNORECASE):
|
|
240
|
+
js = js.strip()
|
|
241
|
+
if not js:
|
|
242
|
+
continue
|
|
243
|
+
# If a full HTML doc was embedded in the fence, extract inner <script> contents
|
|
244
|
+
if "<script" in js or "<!DOCTYPE" in js or "<html" in js:
|
|
245
|
+
scripts = re.findall(r'<script[^>]*>([\s\S]*?)</script>', js, re.IGNORECASE)
|
|
246
|
+
js = "\n".join(s.strip() for s in scripts if s.strip()) or js
|
|
247
|
+
return {"javascript": js, "teaching_steps": [], "notes": None}
|
|
248
|
+
|
|
249
|
+
# 3) Raw HTML with <script>…</script>
|
|
250
|
+
if "<script" in content:
|
|
251
|
+
scripts = re.findall(r'<script[^>]*>([\s\S]*?)</script>', content, re.IGNORECASE)
|
|
252
|
+
if js := "\n".join(s.strip() for s in scripts if s.strip()):
|
|
253
|
+
return {"javascript": js, "teaching_steps": [], "notes": None}
|
|
254
|
+
|
|
255
|
+
return None
|