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,938 @@
|
|
|
1
|
+
from typing import Dict, List, Optional, Any, TypedDict, Union
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pydantic import BaseModel, Field, model_validator
|
|
6
|
+
from .basic import CompletionUsage, ToolCall
|
|
7
|
+
from .outputs import OutputMode
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class SourceDocument:
|
|
12
|
+
"""Enhanced source document information similar to Langchain's format."""
|
|
13
|
+
source: str
|
|
14
|
+
filename: str
|
|
15
|
+
file_path: Optional[str] = None
|
|
16
|
+
source_path: Optional[str] = None
|
|
17
|
+
url: Optional[str] = None
|
|
18
|
+
content_type: Optional[str] = None
|
|
19
|
+
category: Optional[str] = None
|
|
20
|
+
source_type: Optional[str] = None
|
|
21
|
+
source_ext: Optional[str] = None
|
|
22
|
+
page_number: Optional[int] = None
|
|
23
|
+
chunk_id: Optional[str] = None
|
|
24
|
+
parent_document_id: Optional[str] = None
|
|
25
|
+
chunk_index: Optional[int] = None
|
|
26
|
+
score: Optional[float] = None
|
|
27
|
+
metadata: Dict[str, Any] = None
|
|
28
|
+
|
|
29
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
30
|
+
"""Convert to dictionary format."""
|
|
31
|
+
return {
|
|
32
|
+
"source": self.source,
|
|
33
|
+
"filename": self.filename,
|
|
34
|
+
"file_path": self.file_path,
|
|
35
|
+
"source_path": self.source_path,
|
|
36
|
+
"url": self.url,
|
|
37
|
+
"content_type": self.content_type,
|
|
38
|
+
"category": self.category,
|
|
39
|
+
"source_type": self.source_type,
|
|
40
|
+
"source_ext": self.source_ext,
|
|
41
|
+
"page_number": self.page_number,
|
|
42
|
+
"chunk_id": self.chunk_id,
|
|
43
|
+
"parent_document_id": self.parent_document_id,
|
|
44
|
+
"chunk_index": self.chunk_index,
|
|
45
|
+
"score": self.score,
|
|
46
|
+
"metadata": self.metadata or {}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class StreamChunk(BaseModel):
|
|
51
|
+
"""Represents a chunk in a streaming response."""
|
|
52
|
+
content: str
|
|
53
|
+
is_complete: bool = False
|
|
54
|
+
chunk_id: Optional[str] = None
|
|
55
|
+
turn_id: Optional[str] = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class MessageResponse(TypedDict):
|
|
59
|
+
"""Response structure for LLM messages."""
|
|
60
|
+
id: str
|
|
61
|
+
type: str
|
|
62
|
+
role: str
|
|
63
|
+
content: List[Dict[str, Any]]
|
|
64
|
+
model: str
|
|
65
|
+
stop_reason: Optional[str]
|
|
66
|
+
stop_sequence: Optional[str]
|
|
67
|
+
usage: Dict[str, int]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class AIMessage(BaseModel):
|
|
71
|
+
"""Unified AI message response that can handle various output types."""
|
|
72
|
+
|
|
73
|
+
# Core response data
|
|
74
|
+
input: str = Field(
|
|
75
|
+
description="The original input/prompt sent to the LLM"
|
|
76
|
+
)
|
|
77
|
+
output: Any = Field(
|
|
78
|
+
description="The response output - can be text, structured data, dataframe, etc."
|
|
79
|
+
)
|
|
80
|
+
response: Optional[str] = Field(
|
|
81
|
+
default=None,
|
|
82
|
+
description="The textual response from the model, if applicable"
|
|
83
|
+
)
|
|
84
|
+
data: Optional[Any] = Field(
|
|
85
|
+
default=None,
|
|
86
|
+
description="Structured data associated with the response, if applicable"
|
|
87
|
+
)
|
|
88
|
+
code: Optional[str] = Field(
|
|
89
|
+
default=None,
|
|
90
|
+
description="The Python code used for analysis OR the Code generated under request (e.g. JSON definition for a Altair/Vega Chart)."
|
|
91
|
+
)
|
|
92
|
+
images: Optional[List[Path]] = Field(
|
|
93
|
+
default_factory=list,
|
|
94
|
+
description="List of image file paths generated by the model"
|
|
95
|
+
)
|
|
96
|
+
media: Optional[List[Path]] = Field(
|
|
97
|
+
default_factory=list,
|
|
98
|
+
description="List of media files generated by the model"
|
|
99
|
+
)
|
|
100
|
+
files: Optional[List[Path]] = Field(
|
|
101
|
+
default_factory=list,
|
|
102
|
+
description="List of file paths associated with the response"
|
|
103
|
+
)
|
|
104
|
+
documents: Optional[List[Any]] = Field(
|
|
105
|
+
default_factory=list,
|
|
106
|
+
description="List of document file paths associated with the response"
|
|
107
|
+
)
|
|
108
|
+
# Metadata
|
|
109
|
+
model: str = Field(
|
|
110
|
+
description="The model used for generation"
|
|
111
|
+
)
|
|
112
|
+
provider: str = Field(
|
|
113
|
+
description="The LLM provider (openai, groq, claude, etc.)"
|
|
114
|
+
)
|
|
115
|
+
# Usage and performance
|
|
116
|
+
usage: CompletionUsage = Field(
|
|
117
|
+
description="Token usage and timing information"
|
|
118
|
+
)
|
|
119
|
+
# Additional response metadata
|
|
120
|
+
stop_reason: Optional[str] = Field(
|
|
121
|
+
default=None, description="Why the generation stopped"
|
|
122
|
+
)
|
|
123
|
+
finish_reason: Optional[str] = Field(
|
|
124
|
+
default=None, description="Finish reason from provider"
|
|
125
|
+
)
|
|
126
|
+
# Tool usage
|
|
127
|
+
tool_calls: List[ToolCall] = Field(
|
|
128
|
+
default_factory=list,
|
|
129
|
+
description="Tools called during generation"
|
|
130
|
+
)
|
|
131
|
+
# Conversation context
|
|
132
|
+
user_id: Optional[Union[str, int]] = Field(
|
|
133
|
+
default=None, description="User ID for conversation tracking"
|
|
134
|
+
)
|
|
135
|
+
session_id: Optional[str] = Field(
|
|
136
|
+
default=None, description="Session ID for conversation tracking"
|
|
137
|
+
)
|
|
138
|
+
# Timestamps
|
|
139
|
+
created_at: datetime = Field(
|
|
140
|
+
default_factory=datetime.now, description="When the response was created"
|
|
141
|
+
)
|
|
142
|
+
response_time: Optional[float] = Field(
|
|
143
|
+
default=None,
|
|
144
|
+
description="Time taken to generate the response (in seconds)"
|
|
145
|
+
)
|
|
146
|
+
# Raw response for debugging
|
|
147
|
+
raw_response: Optional[Dict[str, Any]] = Field(
|
|
148
|
+
default=None, description="Original response from provider"
|
|
149
|
+
)
|
|
150
|
+
# Conversation turn info
|
|
151
|
+
turn_id: Optional[str] = Field(
|
|
152
|
+
default=None,
|
|
153
|
+
description="Unique ID for this conversation turn"
|
|
154
|
+
)
|
|
155
|
+
# Vector store and conversation history tracking
|
|
156
|
+
used_vector_context: bool = Field(
|
|
157
|
+
default=False,
|
|
158
|
+
description="Whether vector store context was used in generating this response"
|
|
159
|
+
)
|
|
160
|
+
used_conversation_history: bool = Field(
|
|
161
|
+
default=False,
|
|
162
|
+
description="Whether conversation history was used in generating this response"
|
|
163
|
+
)
|
|
164
|
+
vector_context_length: int = Field(
|
|
165
|
+
default=0,
|
|
166
|
+
description="Length of vector context used (in characters)"
|
|
167
|
+
)
|
|
168
|
+
conversation_context_length: int = Field(
|
|
169
|
+
default=0,
|
|
170
|
+
description="Length of conversation context used (in characters)"
|
|
171
|
+
)
|
|
172
|
+
search_results_count: int = Field(
|
|
173
|
+
default=0,
|
|
174
|
+
description="Number of search results retrieved from vector store"
|
|
175
|
+
)
|
|
176
|
+
search_type: Optional[str] = Field(
|
|
177
|
+
default=None,
|
|
178
|
+
description="Type of search performed (similarity, mmr, ensemble, etc.)"
|
|
179
|
+
)
|
|
180
|
+
search_score_threshold: Optional[float] = Field(
|
|
181
|
+
default=None,
|
|
182
|
+
description="Score threshold used for vector search"
|
|
183
|
+
)
|
|
184
|
+
context_sources: Optional[List[str]] = Field(
|
|
185
|
+
default_factory=list,
|
|
186
|
+
description="List of source identifiers for context used"
|
|
187
|
+
)
|
|
188
|
+
source_documents: Optional[List[SourceDocument]] = Field(
|
|
189
|
+
default_factory=list,
|
|
190
|
+
description="List of detailed source documents used for context"
|
|
191
|
+
)
|
|
192
|
+
structured_output: Optional[Union[BaseModel, Any]] = Field(
|
|
193
|
+
default=None,
|
|
194
|
+
description="Immutable original structured output (BaseModel, dataclass, DataFrame)"
|
|
195
|
+
)
|
|
196
|
+
is_structured: bool = Field(
|
|
197
|
+
default=False,
|
|
198
|
+
description="Whether the output is structured"
|
|
199
|
+
)
|
|
200
|
+
metadata: Dict[str, Any] = Field(
|
|
201
|
+
default_factory=dict,
|
|
202
|
+
description="Additional metadata associated with the response"
|
|
203
|
+
)
|
|
204
|
+
output_mode: OutputMode = Field(
|
|
205
|
+
default=OutputMode.DEFAULT,
|
|
206
|
+
description="The output mode used for rendering (markdown, html, json, etc.)"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
class Config:
|
|
210
|
+
"""Pydantic configuration for AIMessage."""
|
|
211
|
+
# Allow arbitrary types for output field (pandas DataFrames, etc.)
|
|
212
|
+
arbitrary_types_allowed = True
|
|
213
|
+
|
|
214
|
+
@property
|
|
215
|
+
def content(self) -> Any:
|
|
216
|
+
"""
|
|
217
|
+
Get content as a string. This is an alias for to_text property
|
|
218
|
+
that provides a more intuitive API and compatibility with standard
|
|
219
|
+
AI message formats.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
str: The text representation of the output
|
|
223
|
+
"""
|
|
224
|
+
return self.output
|
|
225
|
+
|
|
226
|
+
@content.setter
|
|
227
|
+
def content(self, value: Any) -> None:
|
|
228
|
+
"""
|
|
229
|
+
Set content by updating the output field.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
value: The content string to set
|
|
233
|
+
"""
|
|
234
|
+
self.output = value
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def to_text(self) -> str:
|
|
238
|
+
"""Get text representation of output."""
|
|
239
|
+
if self.response:
|
|
240
|
+
return self.response
|
|
241
|
+
if isinstance(self.output, str):
|
|
242
|
+
return self.output
|
|
243
|
+
elif isinstance(self.output, dict) and 'content' in self.output:
|
|
244
|
+
# Handle MessageResponse-style format
|
|
245
|
+
content = self.output['content']
|
|
246
|
+
if isinstance(content, list) and content:
|
|
247
|
+
return content[0].get('text', str(self.output))
|
|
248
|
+
return str(content)
|
|
249
|
+
elif hasattr(self.output, 'to_string'):
|
|
250
|
+
# Handle pandas DataFrames
|
|
251
|
+
return self.output.to_string()
|
|
252
|
+
else:
|
|
253
|
+
# Return the output *as is*
|
|
254
|
+
return str(self.output)
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def has_tools(self) -> bool:
|
|
258
|
+
"""Check if tools were used."""
|
|
259
|
+
return len(self.tool_calls) > 0
|
|
260
|
+
|
|
261
|
+
def add_tool_call(self, tool_call: ToolCall) -> None:
|
|
262
|
+
"""Add a tool call to the response."""
|
|
263
|
+
self.tool_calls.append(tool_call) # pylint: disable=E1101 # noqa
|
|
264
|
+
|
|
265
|
+
# Context Information:
|
|
266
|
+
@property
|
|
267
|
+
def has_context(self) -> bool:
|
|
268
|
+
"""Check if any context (vector or conversation) was used."""
|
|
269
|
+
return self.used_vector_context or self.used_conversation_history
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def context_summary(self) -> Dict[str, Any]:
|
|
273
|
+
"""Get a summary of context usage."""
|
|
274
|
+
return {
|
|
275
|
+
'used_vector_context': self.used_vector_context,
|
|
276
|
+
'used_conversation_history': self.used_conversation_history,
|
|
277
|
+
'vector_context_length': self.vector_context_length,
|
|
278
|
+
'conversation_context_length': self.conversation_context_length,
|
|
279
|
+
'search_results_count': self.search_results_count,
|
|
280
|
+
'search_type': self.search_type,
|
|
281
|
+
'context_sources_count': len(self.context_sources) if self.context_sources else 0
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
def set_vector_context_info(
|
|
285
|
+
self,
|
|
286
|
+
used: bool,
|
|
287
|
+
context_length: int = 0,
|
|
288
|
+
search_results_count: int = 0,
|
|
289
|
+
search_type: Optional[str] = None,
|
|
290
|
+
score_threshold: Optional[float] = None,
|
|
291
|
+
sources: Optional[List[str]] = None,
|
|
292
|
+
source_documents: Optional[List[SourceDocument]] = None
|
|
293
|
+
) -> None:
|
|
294
|
+
"""Set vector context information."""
|
|
295
|
+
self.used_vector_context = used
|
|
296
|
+
self.vector_context_length = context_length
|
|
297
|
+
self.search_results_count = search_results_count
|
|
298
|
+
self.search_type = search_type
|
|
299
|
+
self.search_score_threshold = score_threshold
|
|
300
|
+
if source_documents:
|
|
301
|
+
self.source_documents.extend(source_documents)
|
|
302
|
+
if sources:
|
|
303
|
+
self.context_sources.extend(sources) # pylint: disable=E1101 # noqa
|
|
304
|
+
|
|
305
|
+
def set_conversation_context_info(
|
|
306
|
+
self,
|
|
307
|
+
used: bool,
|
|
308
|
+
context_length: int = 0
|
|
309
|
+
) -> None:
|
|
310
|
+
"""Set conversation context information."""
|
|
311
|
+
self.used_conversation_history = used
|
|
312
|
+
self.conversation_context_length = context_length
|
|
313
|
+
|
|
314
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
315
|
+
"""Convert to dictionary for serialization."""
|
|
316
|
+
return self.model_dump()
|
|
317
|
+
|
|
318
|
+
def get_context_metadata(self) -> Dict[str, Any]:
|
|
319
|
+
"""Get metadata about context usage for logging/analytics."""
|
|
320
|
+
return {
|
|
321
|
+
'context_usage': {
|
|
322
|
+
'vector_context': {
|
|
323
|
+
'used': self.used_vector_context,
|
|
324
|
+
'length': self.vector_context_length,
|
|
325
|
+
'search_results': self.search_results_count,
|
|
326
|
+
'search_type': self.search_type,
|
|
327
|
+
'score_threshold': self.search_score_threshold,
|
|
328
|
+
'sources_count': len(self.context_sources) if self.context_sources else 0
|
|
329
|
+
},
|
|
330
|
+
'conversation_history': {
|
|
331
|
+
'used': self.used_conversation_history,
|
|
332
|
+
'length': self.conversation_context_length
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
'tools': {
|
|
336
|
+
'used': self.has_tools,
|
|
337
|
+
'count': len(self.tool_calls)
|
|
338
|
+
},
|
|
339
|
+
'timing': {
|
|
340
|
+
'created_at': self.created_at.isoformat() # pylint: disable=E1101 # noqa
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
def render_as(
|
|
345
|
+
self,
|
|
346
|
+
mode: OutputMode,
|
|
347
|
+
formatter: Any,
|
|
348
|
+
**kwargs
|
|
349
|
+
) -> 'AIMessage':
|
|
350
|
+
"""
|
|
351
|
+
Create a new AIMessage with different rendering.
|
|
352
|
+
Preserves structured_output.
|
|
353
|
+
"""
|
|
354
|
+
source = self.structured_output if self.is_structured else self.output
|
|
355
|
+
|
|
356
|
+
return AIMessage(
|
|
357
|
+
**{
|
|
358
|
+
**self.model_dump(exclude={'output', 'output_mode'}),
|
|
359
|
+
'output': formatter.format(mode, source, **kwargs),
|
|
360
|
+
'output_mode': mode
|
|
361
|
+
}
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# Factory functions to create AIMessage from different providers
|
|
365
|
+
class AIMessageFactory:
|
|
366
|
+
"""Factory to create AIMessage from different provider responses."""
|
|
367
|
+
|
|
368
|
+
@staticmethod
|
|
369
|
+
def from_completion(
|
|
370
|
+
response: Any,
|
|
371
|
+
input_text: str,
|
|
372
|
+
structured_output: Optional[Any] = None,
|
|
373
|
+
output_mode: OutputMode = OutputMode.DEFAULT,
|
|
374
|
+
formatter: Optional[Any] = None,
|
|
375
|
+
**kwargs
|
|
376
|
+
) -> AIMessage:
|
|
377
|
+
"""Create AIMessage with proper separation of concerns"""
|
|
378
|
+
|
|
379
|
+
# Determine the raw output
|
|
380
|
+
raw_output = structured_output or response.content
|
|
381
|
+
|
|
382
|
+
# Create base message
|
|
383
|
+
message = AIMessage(
|
|
384
|
+
input=input_text,
|
|
385
|
+
output=raw_output, # Initially set to raw
|
|
386
|
+
structured_output=structured_output,
|
|
387
|
+
is_structured=structured_output is not None,
|
|
388
|
+
output_mode=output_mode,
|
|
389
|
+
**kwargs
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
# Apply formatting if needed
|
|
393
|
+
if output_mode != OutputMode.DEFAULT and formatter:
|
|
394
|
+
# Render from structured_output if available, else from output
|
|
395
|
+
source = message.structured_output if message.is_structured else message.output
|
|
396
|
+
message.output = formatter.format(output_mode, source)
|
|
397
|
+
|
|
398
|
+
return message
|
|
399
|
+
|
|
400
|
+
@staticmethod
|
|
401
|
+
def from_openai(
|
|
402
|
+
response: Any,
|
|
403
|
+
input_text: str,
|
|
404
|
+
model: str,
|
|
405
|
+
user_id: Optional[str] = None,
|
|
406
|
+
session_id: Optional[str] = None,
|
|
407
|
+
turn_id: Optional[str] = None,
|
|
408
|
+
structured_output: Any = None
|
|
409
|
+
) -> AIMessage:
|
|
410
|
+
"""Create AIMessage from OpenAI response."""
|
|
411
|
+
message = response.choices[0].message
|
|
412
|
+
|
|
413
|
+
# Handle tool calls
|
|
414
|
+
tool_calls = []
|
|
415
|
+
if hasattr(message, 'tool_calls') and message.tool_calls:
|
|
416
|
+
tool_calls.extend(
|
|
417
|
+
ToolCall(
|
|
418
|
+
id=tc.id,
|
|
419
|
+
name=tc.function.name,
|
|
420
|
+
arguments=(
|
|
421
|
+
tc.function.arguments
|
|
422
|
+
if isinstance(tc.function.arguments, dict)
|
|
423
|
+
else eval(tc.function.arguments)
|
|
424
|
+
),
|
|
425
|
+
)
|
|
426
|
+
for tc in message.tool_calls
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
finish_reason = getattr(response.choices[0], "finish_reason", None)
|
|
430
|
+
stop_reason = getattr(response.choices[0], "stop_reason", None)
|
|
431
|
+
|
|
432
|
+
return AIMessage(
|
|
433
|
+
input=input_text,
|
|
434
|
+
output=structured_output or message.content,
|
|
435
|
+
is_structured=structured_output is not None,
|
|
436
|
+
structured_output=structured_output,
|
|
437
|
+
model=model,
|
|
438
|
+
provider="openai",
|
|
439
|
+
usage=CompletionUsage.from_openai(response.usage),
|
|
440
|
+
stop_reason=stop_reason or finish_reason,
|
|
441
|
+
finish_reason=finish_reason,
|
|
442
|
+
tool_calls=tool_calls,
|
|
443
|
+
user_id=user_id,
|
|
444
|
+
session_id=session_id,
|
|
445
|
+
turn_id=turn_id,
|
|
446
|
+
raw_response=response.dict() if hasattr(response, 'dict') else response.__dict__
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
@staticmethod
|
|
450
|
+
def from_groq(
|
|
451
|
+
response: Any,
|
|
452
|
+
input_text: str,
|
|
453
|
+
model: str,
|
|
454
|
+
user_id: Optional[str] = None,
|
|
455
|
+
session_id: Optional[str] = None,
|
|
456
|
+
turn_id: Optional[str] = None,
|
|
457
|
+
structured_output: Any = None
|
|
458
|
+
) -> AIMessage:
|
|
459
|
+
"""Create AIMessage from Groq response."""
|
|
460
|
+
message = response.choices[0].message
|
|
461
|
+
|
|
462
|
+
# Handle tool calls
|
|
463
|
+
tool_calls = []
|
|
464
|
+
if hasattr(message, 'tool_calls') and message.tool_calls:
|
|
465
|
+
tool_calls.extend(
|
|
466
|
+
ToolCall(
|
|
467
|
+
id=tc.id,
|
|
468
|
+
name=tc.function.name,
|
|
469
|
+
arguments=(
|
|
470
|
+
tc.function.arguments
|
|
471
|
+
if isinstance(tc.function.arguments, dict)
|
|
472
|
+
else eval(tc.function.arguments)
|
|
473
|
+
),
|
|
474
|
+
)
|
|
475
|
+
for tc in message.tool_calls
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
return AIMessage(
|
|
479
|
+
input=input_text,
|
|
480
|
+
output=structured_output or message.content,
|
|
481
|
+
is_structured=structured_output is not None,
|
|
482
|
+
structured_output=structured_output,
|
|
483
|
+
model=model,
|
|
484
|
+
provider="groq",
|
|
485
|
+
usage=CompletionUsage.from_groq(response.usage),
|
|
486
|
+
stop_reason=response.choices[0].finish_reason,
|
|
487
|
+
finish_reason=response.choices[0].finish_reason,
|
|
488
|
+
tool_calls=tool_calls,
|
|
489
|
+
user_id=user_id,
|
|
490
|
+
session_id=session_id,
|
|
491
|
+
turn_id=turn_id,
|
|
492
|
+
raw_response=response.dict() if hasattr(response, 'dict') else response.__dict__,
|
|
493
|
+
response=message.content
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
@staticmethod
|
|
497
|
+
def from_grok(
|
|
498
|
+
response: Any,
|
|
499
|
+
input_text: str,
|
|
500
|
+
model: str,
|
|
501
|
+
user_id: Optional[str] = None,
|
|
502
|
+
session_id: Optional[str] = None,
|
|
503
|
+
turn_id: Optional[str] = None,
|
|
504
|
+
structured_output: Any = None
|
|
505
|
+
) -> AIMessage:
|
|
506
|
+
"""Create AIMessage from Grok response."""
|
|
507
|
+
# xAI SDK response structure needs verification, assuming OpenAI-like for now based on 'usage'
|
|
508
|
+
# If response is a Pydantic model from structured output, content might be parsed.
|
|
509
|
+
|
|
510
|
+
content = ""
|
|
511
|
+
tool_calls = []
|
|
512
|
+
finish_reason = None
|
|
513
|
+
|
|
514
|
+
# Check if it's a simple text response or object
|
|
515
|
+
if hasattr(response, 'content'):
|
|
516
|
+
content = response.content
|
|
517
|
+
elif isinstance(response, dict) and 'content' in response:
|
|
518
|
+
content = response['content']
|
|
519
|
+
|
|
520
|
+
# Try to extract standard fields if present
|
|
521
|
+
if hasattr(response, 'tool_calls') and response.tool_calls:
|
|
522
|
+
# Map tool calls
|
|
523
|
+
tool_calls.extend(
|
|
524
|
+
ToolCall(
|
|
525
|
+
id=tc.id,
|
|
526
|
+
name=tc.function.name,
|
|
527
|
+
arguments=(
|
|
528
|
+
tc.function.arguments
|
|
529
|
+
if isinstance(tc.function.arguments, dict)
|
|
530
|
+
else eval(tc.function.arguments)
|
|
531
|
+
),
|
|
532
|
+
)
|
|
533
|
+
for tc in response.tool_calls
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
return AIMessage(
|
|
537
|
+
input=input_text,
|
|
538
|
+
output=structured_output or content,
|
|
539
|
+
is_structured=structured_output is not None,
|
|
540
|
+
structured_output=structured_output,
|
|
541
|
+
model=model,
|
|
542
|
+
provider="grok",
|
|
543
|
+
usage=CompletionUsage.from_grok(response.usage) if hasattr(response, 'usage') else CompletionUsage(),
|
|
544
|
+
stop_reason=finish_reason,
|
|
545
|
+
finish_reason=finish_reason,
|
|
546
|
+
tool_calls=tool_calls,
|
|
547
|
+
user_id=user_id,
|
|
548
|
+
session_id=session_id,
|
|
549
|
+
turn_id=turn_id,
|
|
550
|
+
raw_response=response.__dict__ if hasattr(response, '__dict__') else response,
|
|
551
|
+
response=content
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
@staticmethod
|
|
555
|
+
def from_claude(
|
|
556
|
+
response: Dict[str, Any],
|
|
557
|
+
input_text: str,
|
|
558
|
+
model: str,
|
|
559
|
+
user_id: Optional[str] = None,
|
|
560
|
+
session_id: Optional[str] = None,
|
|
561
|
+
turn_id: Optional[str] = None,
|
|
562
|
+
structured_output: Any = None,
|
|
563
|
+
tool_calls: List[ToolCall] = None
|
|
564
|
+
) -> AIMessage:
|
|
565
|
+
"""Create AIMessage from Claude response."""
|
|
566
|
+
# Extract text content
|
|
567
|
+
content = ""
|
|
568
|
+
for content_block in response.get("content", []):
|
|
569
|
+
if content_block.get("type") == "text":
|
|
570
|
+
content += content_block.get("text", "")
|
|
571
|
+
|
|
572
|
+
return AIMessage(
|
|
573
|
+
input=input_text,
|
|
574
|
+
output=structured_output or content,
|
|
575
|
+
is_structured=structured_output is not None,
|
|
576
|
+
structured_output=structured_output,
|
|
577
|
+
model=model,
|
|
578
|
+
provider="claude",
|
|
579
|
+
usage=CompletionUsage.from_claude(response.get("usage", {})),
|
|
580
|
+
stop_reason=response.get("stop_reason"),
|
|
581
|
+
finish_reason=response.get("stop_reason"),
|
|
582
|
+
tool_calls=tool_calls or [],
|
|
583
|
+
user_id=user_id,
|
|
584
|
+
session_id=session_id,
|
|
585
|
+
turn_id=turn_id,
|
|
586
|
+
raw_response=response,
|
|
587
|
+
response=content if isinstance(content, str) else str(content)
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
@staticmethod
|
|
591
|
+
def from_gemini(
|
|
592
|
+
response: Any,
|
|
593
|
+
input_text: str,
|
|
594
|
+
model: str,
|
|
595
|
+
user_id: Optional[str] = None,
|
|
596
|
+
session_id: Optional[str] = None,
|
|
597
|
+
turn_id: Optional[str] = None,
|
|
598
|
+
structured_output: Any = None,
|
|
599
|
+
tool_calls: List[ToolCall] = None,
|
|
600
|
+
# Add these new parameters:
|
|
601
|
+
conversation_history: Optional[Any] = None,
|
|
602
|
+
text_response: Optional[str] = None,
|
|
603
|
+
files: Optional[List[Path]] = None
|
|
604
|
+
) -> AIMessage:
|
|
605
|
+
"""Create AIMessage from Gemini/Vertex AI response."""
|
|
606
|
+
# Handle both direct text responses and response objects
|
|
607
|
+
if text_response:
|
|
608
|
+
content = text_response
|
|
609
|
+
elif hasattr(response, 'text'):
|
|
610
|
+
content = response.text
|
|
611
|
+
else:
|
|
612
|
+
content = str(response)
|
|
613
|
+
|
|
614
|
+
# Extract usage information
|
|
615
|
+
usage_dict = {}
|
|
616
|
+
if hasattr(response, 'usage_metadata') and response.usage_metadata:
|
|
617
|
+
# Vertex AI format
|
|
618
|
+
usage_dict = {
|
|
619
|
+
'prompt_token_count': response.usage_metadata.prompt_token_count,
|
|
620
|
+
'candidates_token_count': response.usage_metadata.candidates_token_count,
|
|
621
|
+
'total_token_count': response.usage_metadata.total_token_count
|
|
622
|
+
}
|
|
623
|
+
elif hasattr(response, 'usage'):
|
|
624
|
+
# Standard Gemini API format
|
|
625
|
+
usage_dict = response.usage.__dict__ if hasattr(response.usage, '__dict__') else {}
|
|
626
|
+
|
|
627
|
+
ai_message = AIMessage(
|
|
628
|
+
input=input_text,
|
|
629
|
+
output=structured_output or content,
|
|
630
|
+
is_structured=structured_output is not None,
|
|
631
|
+
structured_output=structured_output,
|
|
632
|
+
model=model,
|
|
633
|
+
provider="gemini", # Will be overridden to "vertex_ai" in VertexAIClient
|
|
634
|
+
usage=CompletionUsage.from_gemini(usage_dict),
|
|
635
|
+
stop_reason="completed",
|
|
636
|
+
finish_reason="completed",
|
|
637
|
+
tool_calls=tool_calls or [],
|
|
638
|
+
user_id=user_id,
|
|
639
|
+
session_id=session_id,
|
|
640
|
+
turn_id=turn_id,
|
|
641
|
+
raw_response=response.__dict__ if hasattr(response, '__dict__') else str(response),
|
|
642
|
+
response=content,
|
|
643
|
+
files=files or [],
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
if conversation_history:
|
|
647
|
+
ai_message.used_conversation_history = True
|
|
648
|
+
ai_message.conversation_context_length = len(conversation_history.turns) if hasattr(conversation_history, 'turns') else 0
|
|
649
|
+
|
|
650
|
+
return ai_message
|
|
651
|
+
|
|
652
|
+
@staticmethod
|
|
653
|
+
def create_message(
|
|
654
|
+
response: Any,
|
|
655
|
+
input_text: str,
|
|
656
|
+
model: str,
|
|
657
|
+
user_id: Optional[str] = None,
|
|
658
|
+
session_id: Optional[str] = None,
|
|
659
|
+
turn_id: Optional[str] = None,
|
|
660
|
+
structured_output: Any = None,
|
|
661
|
+
tool_calls: List[ToolCall] = None,
|
|
662
|
+
# Add these new parameters:
|
|
663
|
+
conversation_history: Optional[Any] = None,
|
|
664
|
+
text_response: Optional[str] = None,
|
|
665
|
+
usage: Optional[CompletionUsage] = None,
|
|
666
|
+
response_time: Optional[float] = None
|
|
667
|
+
) -> AIMessage:
|
|
668
|
+
"""Create AIMessage from any provider response."""
|
|
669
|
+
if hasattr(response, 'provider'):
|
|
670
|
+
provider = response.provider.lower()
|
|
671
|
+
else:
|
|
672
|
+
provider = "unknown"
|
|
673
|
+
|
|
674
|
+
ai_message = AIMessage(
|
|
675
|
+
input=input_text,
|
|
676
|
+
output=structured_output,
|
|
677
|
+
is_structured=structured_output is not None,
|
|
678
|
+
structured_output=structured_output,
|
|
679
|
+
model=model,
|
|
680
|
+
provider=provider,
|
|
681
|
+
stop_reason="completed",
|
|
682
|
+
finish_reason="completed",
|
|
683
|
+
tool_calls=tool_calls or [],
|
|
684
|
+
user_id=user_id,
|
|
685
|
+
session_id=session_id,
|
|
686
|
+
turn_id=turn_id,
|
|
687
|
+
raw_response=response.__dict__ if hasattr(response, '__dict__') else {"result": text_response},
|
|
688
|
+
response=text_response,
|
|
689
|
+
usage=usage or CompletionUsage.from_response(response),
|
|
690
|
+
response_time=response_time
|
|
691
|
+
)
|
|
692
|
+
if conversation_history:
|
|
693
|
+
ai_message.used_conversation_history = True
|
|
694
|
+
ai_message.conversation_context_length = len(conversation_history.turns) if hasattr(conversation_history, 'turns') else 0
|
|
695
|
+
|
|
696
|
+
return ai_message
|
|
697
|
+
|
|
698
|
+
@staticmethod
|
|
699
|
+
def from_imagen(**kwargs):
|
|
700
|
+
return AIMessage(**kwargs)
|
|
701
|
+
|
|
702
|
+
@staticmethod
|
|
703
|
+
def from_speech(**kwargs):
|
|
704
|
+
return AIMessage(**kwargs)
|
|
705
|
+
|
|
706
|
+
@staticmethod
|
|
707
|
+
def from_video(**kwargs):
|
|
708
|
+
return AIMessage(**kwargs)
|
|
709
|
+
|
|
710
|
+
class AgentResponse(BaseModel):
|
|
711
|
+
"""
|
|
712
|
+
AgentResponse is a model that defines the structure of the response for Any Parrot agent.
|
|
713
|
+
"""
|
|
714
|
+
session_id: Optional[str] = Field(
|
|
715
|
+
default=None,
|
|
716
|
+
description="Unique identifier for the session"
|
|
717
|
+
)
|
|
718
|
+
user_id: Optional[str] = Field(
|
|
719
|
+
default=None,
|
|
720
|
+
description="Unique identifier for the user"
|
|
721
|
+
)
|
|
722
|
+
agent_id: Optional[str] = Field(
|
|
723
|
+
default=None,
|
|
724
|
+
description="Unique identifier for the agent that processed the request"
|
|
725
|
+
)
|
|
726
|
+
agent_name: Optional[str] = Field(
|
|
727
|
+
default="Agentic",
|
|
728
|
+
description="Name of the agent that processed the request"
|
|
729
|
+
)
|
|
730
|
+
status: str = Field(default="success", description="Status of the response")
|
|
731
|
+
question: Optional[str] = Field(
|
|
732
|
+
default=None,
|
|
733
|
+
description="Question made by User"
|
|
734
|
+
)
|
|
735
|
+
response: Optional[AIMessage] = Field(
|
|
736
|
+
default=None,
|
|
737
|
+
description="Response returned by the agent"
|
|
738
|
+
)
|
|
739
|
+
data: Optional[str] = Field(
|
|
740
|
+
default=None,
|
|
741
|
+
description="Data returned by the agent, can be text, JSON, etc."
|
|
742
|
+
)
|
|
743
|
+
# Optional output field for structured data
|
|
744
|
+
output: Optional[Any] = Field(
|
|
745
|
+
default=None,
|
|
746
|
+
description="Output of the agent's processing"
|
|
747
|
+
)
|
|
748
|
+
attributes: Dict[str, str] = Field(
|
|
749
|
+
default_factory=dict,
|
|
750
|
+
description="Attributes associated with the response"
|
|
751
|
+
)
|
|
752
|
+
# Timestamp
|
|
753
|
+
created_at: datetime = Field(
|
|
754
|
+
default_factory=datetime.now, description="Timestamp when response was created"
|
|
755
|
+
)
|
|
756
|
+
# Optional file paths
|
|
757
|
+
transcript: Optional[str] = Field(
|
|
758
|
+
default=None, description="Transcript of the conversation with the agent"
|
|
759
|
+
)
|
|
760
|
+
script_path: Optional[str] = Field(
|
|
761
|
+
default=None, description="Path to the conversational script associated with the session"
|
|
762
|
+
)
|
|
763
|
+
podcast_path: Optional[str] = Field(
|
|
764
|
+
default=None, description="Path to the podcast associated with the session"
|
|
765
|
+
)
|
|
766
|
+
pdf_path: Optional[str] = Field(
|
|
767
|
+
default=None, description="Path to the PDF associated with the session"
|
|
768
|
+
)
|
|
769
|
+
document_path: Optional[str] = Field(
|
|
770
|
+
default=None, description="Path to any document generated during session"
|
|
771
|
+
)
|
|
772
|
+
images: Optional[List[Path]] = Field(
|
|
773
|
+
default_factory=list,
|
|
774
|
+
description="List of image file paths generated by the model"
|
|
775
|
+
)
|
|
776
|
+
media: Optional[List[Path]] = Field(
|
|
777
|
+
default_factory=list,
|
|
778
|
+
description="List of media file paths generated by the model"
|
|
779
|
+
)
|
|
780
|
+
documents: Optional[List[Any]] = Field(
|
|
781
|
+
default_factory=list,
|
|
782
|
+
description="List of document file paths associated with the response"
|
|
783
|
+
)
|
|
784
|
+
# complete list of generated files:
|
|
785
|
+
files: List[str] = Field(
|
|
786
|
+
default_factory=list,
|
|
787
|
+
description="List of files generated during the session"
|
|
788
|
+
)
|
|
789
|
+
turn_id: Optional[str] = Field(
|
|
790
|
+
default=None, description="Unique identifier for the conversation turn"
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
class Config:
|
|
794
|
+
"""Pydantic configuration for AgentResponse."""
|
|
795
|
+
# Allow arbitrary types for output field (pandas DataFrames, etc.)
|
|
796
|
+
arbitrary_types_allowed = False
|
|
797
|
+
# Allow extra fields if needed
|
|
798
|
+
extra = "allow"
|
|
799
|
+
# Use enum values
|
|
800
|
+
use_enum_values = True
|
|
801
|
+
# Validate assignment
|
|
802
|
+
validate_assignment = True
|
|
803
|
+
|
|
804
|
+
@property
|
|
805
|
+
def content(self) -> Any:
|
|
806
|
+
"""
|
|
807
|
+
Get content as a string. This is an alias for to_text property
|
|
808
|
+
that provides a more intuitive API and compatibility with standard
|
|
809
|
+
AI message formats.
|
|
810
|
+
|
|
811
|
+
Returns:
|
|
812
|
+
str: The text representation of the output
|
|
813
|
+
"""
|
|
814
|
+
return self.output
|
|
815
|
+
|
|
816
|
+
@content.setter
|
|
817
|
+
def content(self, value: Any) -> None:
|
|
818
|
+
"""
|
|
819
|
+
Set content by updating the output field.
|
|
820
|
+
|
|
821
|
+
Args:
|
|
822
|
+
value: The content string to set
|
|
823
|
+
"""
|
|
824
|
+
self.output = value
|
|
825
|
+
|
|
826
|
+
@model_validator(mode='after')
|
|
827
|
+
def sync_documents_and_paths(self):
|
|
828
|
+
"""
|
|
829
|
+
Automatically populate documents list from individual path fields
|
|
830
|
+
and sync documents from AIMessage if present.
|
|
831
|
+
"""
|
|
832
|
+
# Sync from AIMessage if it exists
|
|
833
|
+
if self.response and isinstance(self.response, AIMessage):
|
|
834
|
+
if hasattr(self.response, 'documents') and self.response.documents:
|
|
835
|
+
# Add AIMessage documents to our documents list
|
|
836
|
+
for doc in self.response.documents:
|
|
837
|
+
if doc not in self.documents:
|
|
838
|
+
self.documents.append(doc)
|
|
839
|
+
|
|
840
|
+
if hasattr(self.response, 'files') and self.response.files:
|
|
841
|
+
# Add AIMessage files to our files list
|
|
842
|
+
for file in self.response.files:
|
|
843
|
+
if file not in self.files:
|
|
844
|
+
self.files.append(file)
|
|
845
|
+
|
|
846
|
+
if hasattr(self.response, 'images') and self.response.images:
|
|
847
|
+
# Add AIMessage images to our images list
|
|
848
|
+
for img in self.response.images:
|
|
849
|
+
if img not in self.images:
|
|
850
|
+
self.images.append(img)
|
|
851
|
+
|
|
852
|
+
if hasattr(self.response, 'media') and self.response.media:
|
|
853
|
+
# Add AIMessage media to our media list
|
|
854
|
+
for med in self.response.media:
|
|
855
|
+
if med not in self.media:
|
|
856
|
+
self.media.append(med)
|
|
857
|
+
|
|
858
|
+
# Auto-add individual paths to appropriate collections
|
|
859
|
+
path_mappings = [
|
|
860
|
+
(self.script_path, self.documents, 'script'),
|
|
861
|
+
(self.podcast_path, self.files, 'podcast'),
|
|
862
|
+
(self.pdf_path, self.documents, 'pdf'),
|
|
863
|
+
(self.document_path, self.documents, 'document'),
|
|
864
|
+
]
|
|
865
|
+
|
|
866
|
+
for path, collection, path_type in path_mappings:
|
|
867
|
+
if path and path not in collection:
|
|
868
|
+
collection.append(path)
|
|
869
|
+
|
|
870
|
+
return self
|
|
871
|
+
|
|
872
|
+
def add_document(self, path: Union[str, Path]) -> None:
|
|
873
|
+
"""
|
|
874
|
+
Add a document to the documents list also document path.
|
|
875
|
+
|
|
876
|
+
Args:
|
|
877
|
+
path: Path to the document
|
|
878
|
+
"""
|
|
879
|
+
path_str = str(path)
|
|
880
|
+
if path_str not in self.documents:
|
|
881
|
+
self.documents.append(path_str)
|
|
882
|
+
self.document_path = path_str
|
|
883
|
+
self.add_file(path_str)
|
|
884
|
+
|
|
885
|
+
def add_file(self, path: Union[str, Path]) -> None:
|
|
886
|
+
"""
|
|
887
|
+
Add a file to the files list.
|
|
888
|
+
|
|
889
|
+
Args:
|
|
890
|
+
path: Path to the file
|
|
891
|
+
"""
|
|
892
|
+
path_str = str(path)
|
|
893
|
+
if path_str not in self.files:
|
|
894
|
+
self.files.append(path_str)
|
|
895
|
+
|
|
896
|
+
def add_image(self, path: Union[str, Path]) -> None:
|
|
897
|
+
"""
|
|
898
|
+
Add an image to the images list.
|
|
899
|
+
|
|
900
|
+
Args:
|
|
901
|
+
path: Path to the image
|
|
902
|
+
"""
|
|
903
|
+
path_str = str(path)
|
|
904
|
+
if path_str not in self.images:
|
|
905
|
+
self.images.append(path_str)
|
|
906
|
+
self.add_file(path_str)
|
|
907
|
+
|
|
908
|
+
def add_media(self, path: Union[str, Path]) -> None:
|
|
909
|
+
"""
|
|
910
|
+
Add a media file to the media list.
|
|
911
|
+
|
|
912
|
+
Args:
|
|
913
|
+
path: Path to the media file
|
|
914
|
+
"""
|
|
915
|
+
path_str = str(path)
|
|
916
|
+
if path_str not in self.media:
|
|
917
|
+
self.media.append(path_str)
|
|
918
|
+
self.add_file(path_str)
|
|
919
|
+
|
|
920
|
+
def set_podcast_path(self, path: Union[str, Path]) -> None:
|
|
921
|
+
"""
|
|
922
|
+
Set the podcast_path and automatically add to files list.
|
|
923
|
+
|
|
924
|
+
Args:
|
|
925
|
+
path: Path to the podcast
|
|
926
|
+
"""
|
|
927
|
+
self.podcast_path = str(path)
|
|
928
|
+
self.add_file(path)
|
|
929
|
+
|
|
930
|
+
def set_pdf_path(self, path: Union[str, Path]) -> None:
|
|
931
|
+
"""
|
|
932
|
+
Set the pdf_path and automatically add to documents list.
|
|
933
|
+
|
|
934
|
+
Args:
|
|
935
|
+
path: Path to the PDF
|
|
936
|
+
"""
|
|
937
|
+
self.pdf_path = str(path)
|
|
938
|
+
self.add_document(path)
|