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.
Files changed (535) hide show
  1. agentui/.prettierrc +15 -0
  2. agentui/QUICKSTART.md +272 -0
  3. agentui/README.md +59 -0
  4. agentui/env.example +16 -0
  5. agentui/jsconfig.json +14 -0
  6. agentui/package-lock.json +4242 -0
  7. agentui/package.json +34 -0
  8. agentui/scripts/postinstall/apply-patches.mjs +260 -0
  9. agentui/src/app.css +61 -0
  10. agentui/src/app.d.ts +13 -0
  11. agentui/src/app.html +12 -0
  12. agentui/src/components/LoadingSpinner.svelte +64 -0
  13. agentui/src/components/ThemeSwitcher.svelte +159 -0
  14. agentui/src/components/index.js +4 -0
  15. agentui/src/lib/api/bots.ts +60 -0
  16. agentui/src/lib/api/chat.ts +22 -0
  17. agentui/src/lib/api/http.ts +25 -0
  18. agentui/src/lib/components/BotCard.svelte +33 -0
  19. agentui/src/lib/components/ChatBubble.svelte +63 -0
  20. agentui/src/lib/components/Toast.svelte +21 -0
  21. agentui/src/lib/config.ts +20 -0
  22. agentui/src/lib/stores/auth.svelte.ts +73 -0
  23. agentui/src/lib/stores/theme.svelte.js +64 -0
  24. agentui/src/lib/stores/toast.svelte.ts +31 -0
  25. agentui/src/lib/utils/conversation.ts +39 -0
  26. agentui/src/routes/+layout.svelte +20 -0
  27. agentui/src/routes/+page.svelte +232 -0
  28. agentui/src/routes/login/+page.svelte +200 -0
  29. agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
  30. agentui/src/routes/talk/[agentId]/+page.ts +7 -0
  31. agentui/static/README.md +1 -0
  32. agentui/svelte.config.js +11 -0
  33. agentui/tailwind.config.ts +53 -0
  34. agentui/tsconfig.json +3 -0
  35. agentui/vite.config.ts +10 -0
  36. ai_parrot-0.17.2.dist-info/METADATA +472 -0
  37. ai_parrot-0.17.2.dist-info/RECORD +535 -0
  38. ai_parrot-0.17.2.dist-info/WHEEL +6 -0
  39. ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
  40. ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
  41. ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
  42. crew-builder/.prettierrc +15 -0
  43. crew-builder/QUICKSTART.md +259 -0
  44. crew-builder/README.md +113 -0
  45. crew-builder/env.example +17 -0
  46. crew-builder/jsconfig.json +14 -0
  47. crew-builder/package-lock.json +4182 -0
  48. crew-builder/package.json +37 -0
  49. crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
  50. crew-builder/src/app.css +62 -0
  51. crew-builder/src/app.d.ts +13 -0
  52. crew-builder/src/app.html +12 -0
  53. crew-builder/src/components/LoadingSpinner.svelte +64 -0
  54. crew-builder/src/components/ThemeSwitcher.svelte +149 -0
  55. crew-builder/src/components/index.js +9 -0
  56. crew-builder/src/lib/api/bots.ts +60 -0
  57. crew-builder/src/lib/api/chat.ts +80 -0
  58. crew-builder/src/lib/api/client.ts +56 -0
  59. crew-builder/src/lib/api/crew/crew.ts +136 -0
  60. crew-builder/src/lib/api/index.ts +5 -0
  61. crew-builder/src/lib/api/o365/auth.ts +65 -0
  62. crew-builder/src/lib/auth/auth.ts +54 -0
  63. crew-builder/src/lib/components/AgentNode.svelte +43 -0
  64. crew-builder/src/lib/components/BotCard.svelte +33 -0
  65. crew-builder/src/lib/components/ChatBubble.svelte +67 -0
  66. crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
  67. crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
  68. crew-builder/src/lib/components/JsonViewer.svelte +24 -0
  69. crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
  70. crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
  71. crew-builder/src/lib/components/Toast.svelte +67 -0
  72. crew-builder/src/lib/components/Toolbar.svelte +157 -0
  73. crew-builder/src/lib/components/index.ts +10 -0
  74. crew-builder/src/lib/config.ts +8 -0
  75. crew-builder/src/lib/stores/auth.svelte.ts +228 -0
  76. crew-builder/src/lib/stores/crewStore.ts +369 -0
  77. crew-builder/src/lib/stores/theme.svelte.js +145 -0
  78. crew-builder/src/lib/stores/toast.svelte.ts +69 -0
  79. crew-builder/src/lib/utils/conversation.ts +39 -0
  80. crew-builder/src/lib/utils/markdown.ts +122 -0
  81. crew-builder/src/lib/utils/talkHistory.ts +47 -0
  82. crew-builder/src/routes/+layout.svelte +20 -0
  83. crew-builder/src/routes/+page.svelte +539 -0
  84. crew-builder/src/routes/agents/+page.svelte +247 -0
  85. crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
  86. crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
  87. crew-builder/src/routes/builder/+page.svelte +204 -0
  88. crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
  89. crew-builder/src/routes/crew/ask/+page.ts +1 -0
  90. crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
  91. crew-builder/src/routes/login/+page.svelte +197 -0
  92. crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
  93. crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
  94. crew-builder/static/README.md +1 -0
  95. crew-builder/svelte.config.js +11 -0
  96. crew-builder/tailwind.config.ts +53 -0
  97. crew-builder/tsconfig.json +3 -0
  98. crew-builder/vite.config.ts +10 -0
  99. mcp_servers/calculator_server.py +309 -0
  100. parrot/__init__.py +27 -0
  101. parrot/__pycache__/__init__.cpython-310.pyc +0 -0
  102. parrot/__pycache__/version.cpython-310.pyc +0 -0
  103. parrot/_version.py +34 -0
  104. parrot/a2a/__init__.py +48 -0
  105. parrot/a2a/client.py +658 -0
  106. parrot/a2a/discovery.py +89 -0
  107. parrot/a2a/mixin.py +257 -0
  108. parrot/a2a/models.py +376 -0
  109. parrot/a2a/server.py +770 -0
  110. parrot/agents/__init__.py +29 -0
  111. parrot/bots/__init__.py +12 -0
  112. parrot/bots/a2a_agent.py +19 -0
  113. parrot/bots/abstract.py +3139 -0
  114. parrot/bots/agent.py +1129 -0
  115. parrot/bots/basic.py +9 -0
  116. parrot/bots/chatbot.py +669 -0
  117. parrot/bots/data.py +1618 -0
  118. parrot/bots/database/__init__.py +5 -0
  119. parrot/bots/database/abstract.py +3071 -0
  120. parrot/bots/database/cache.py +286 -0
  121. parrot/bots/database/models.py +468 -0
  122. parrot/bots/database/prompts.py +154 -0
  123. parrot/bots/database/retries.py +98 -0
  124. parrot/bots/database/router.py +269 -0
  125. parrot/bots/database/sql.py +41 -0
  126. parrot/bots/db/__init__.py +6 -0
  127. parrot/bots/db/abstract.py +556 -0
  128. parrot/bots/db/bigquery.py +602 -0
  129. parrot/bots/db/cache.py +85 -0
  130. parrot/bots/db/documentdb.py +668 -0
  131. parrot/bots/db/elastic.py +1014 -0
  132. parrot/bots/db/influx.py +898 -0
  133. parrot/bots/db/mock.py +96 -0
  134. parrot/bots/db/multi.py +783 -0
  135. parrot/bots/db/prompts.py +185 -0
  136. parrot/bots/db/sql.py +1255 -0
  137. parrot/bots/db/tools.py +212 -0
  138. parrot/bots/document.py +680 -0
  139. parrot/bots/hrbot.py +15 -0
  140. parrot/bots/kb.py +170 -0
  141. parrot/bots/mcp.py +36 -0
  142. parrot/bots/orchestration/README.md +463 -0
  143. parrot/bots/orchestration/__init__.py +1 -0
  144. parrot/bots/orchestration/agent.py +155 -0
  145. parrot/bots/orchestration/crew.py +3330 -0
  146. parrot/bots/orchestration/fsm.py +1179 -0
  147. parrot/bots/orchestration/hr.py +434 -0
  148. parrot/bots/orchestration/storage/__init__.py +4 -0
  149. parrot/bots/orchestration/storage/memory.py +100 -0
  150. parrot/bots/orchestration/storage/mixin.py +119 -0
  151. parrot/bots/orchestration/verify.py +202 -0
  152. parrot/bots/product.py +204 -0
  153. parrot/bots/prompts/__init__.py +96 -0
  154. parrot/bots/prompts/agents.py +155 -0
  155. parrot/bots/prompts/data.py +216 -0
  156. parrot/bots/prompts/output_generation.py +8 -0
  157. parrot/bots/scraper/__init__.py +3 -0
  158. parrot/bots/scraper/models.py +122 -0
  159. parrot/bots/scraper/scraper.py +1173 -0
  160. parrot/bots/scraper/templates.py +115 -0
  161. parrot/bots/stores/__init__.py +5 -0
  162. parrot/bots/stores/local.py +172 -0
  163. parrot/bots/webdev.py +81 -0
  164. parrot/cli.py +17 -0
  165. parrot/clients/__init__.py +16 -0
  166. parrot/clients/base.py +1491 -0
  167. parrot/clients/claude.py +1191 -0
  168. parrot/clients/factory.py +129 -0
  169. parrot/clients/google.py +4567 -0
  170. parrot/clients/gpt.py +1975 -0
  171. parrot/clients/grok.py +432 -0
  172. parrot/clients/groq.py +986 -0
  173. parrot/clients/hf.py +582 -0
  174. parrot/clients/models.py +18 -0
  175. parrot/conf.py +395 -0
  176. parrot/embeddings/__init__.py +9 -0
  177. parrot/embeddings/base.py +157 -0
  178. parrot/embeddings/google.py +98 -0
  179. parrot/embeddings/huggingface.py +74 -0
  180. parrot/embeddings/openai.py +84 -0
  181. parrot/embeddings/processor.py +88 -0
  182. parrot/exceptions.c +13868 -0
  183. parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
  184. parrot/exceptions.pxd +22 -0
  185. parrot/exceptions.pxi +15 -0
  186. parrot/exceptions.pyx +44 -0
  187. parrot/generators/__init__.py +29 -0
  188. parrot/generators/base.py +200 -0
  189. parrot/generators/html.py +293 -0
  190. parrot/generators/react.py +205 -0
  191. parrot/generators/streamlit.py +203 -0
  192. parrot/generators/template.py +105 -0
  193. parrot/handlers/__init__.py +4 -0
  194. parrot/handlers/agent.py +861 -0
  195. parrot/handlers/agents/__init__.py +1 -0
  196. parrot/handlers/agents/abstract.py +900 -0
  197. parrot/handlers/bots.py +338 -0
  198. parrot/handlers/chat.py +915 -0
  199. parrot/handlers/creation.sql +192 -0
  200. parrot/handlers/crew/ARCHITECTURE.md +362 -0
  201. parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
  202. parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
  203. parrot/handlers/crew/__init__.py +0 -0
  204. parrot/handlers/crew/handler.py +801 -0
  205. parrot/handlers/crew/models.py +229 -0
  206. parrot/handlers/crew/redis_persistence.py +523 -0
  207. parrot/handlers/jobs/__init__.py +10 -0
  208. parrot/handlers/jobs/job.py +384 -0
  209. parrot/handlers/jobs/mixin.py +627 -0
  210. parrot/handlers/jobs/models.py +115 -0
  211. parrot/handlers/jobs/worker.py +31 -0
  212. parrot/handlers/models.py +596 -0
  213. parrot/handlers/o365_auth.py +105 -0
  214. parrot/handlers/stream.py +337 -0
  215. parrot/interfaces/__init__.py +6 -0
  216. parrot/interfaces/aws.py +143 -0
  217. parrot/interfaces/credentials.py +113 -0
  218. parrot/interfaces/database.py +27 -0
  219. parrot/interfaces/google.py +1123 -0
  220. parrot/interfaces/hierarchy.py +1227 -0
  221. parrot/interfaces/http.py +651 -0
  222. parrot/interfaces/images/__init__.py +0 -0
  223. parrot/interfaces/images/plugins/__init__.py +24 -0
  224. parrot/interfaces/images/plugins/abstract.py +58 -0
  225. parrot/interfaces/images/plugins/analisys.py +148 -0
  226. parrot/interfaces/images/plugins/classify.py +150 -0
  227. parrot/interfaces/images/plugins/classifybase.py +182 -0
  228. parrot/interfaces/images/plugins/detect.py +150 -0
  229. parrot/interfaces/images/plugins/exif.py +1103 -0
  230. parrot/interfaces/images/plugins/hash.py +52 -0
  231. parrot/interfaces/images/plugins/vision.py +104 -0
  232. parrot/interfaces/images/plugins/yolo.py +66 -0
  233. parrot/interfaces/images/plugins/zerodetect.py +197 -0
  234. parrot/interfaces/o365.py +978 -0
  235. parrot/interfaces/onedrive.py +822 -0
  236. parrot/interfaces/sharepoint.py +1435 -0
  237. parrot/interfaces/soap.py +257 -0
  238. parrot/loaders/__init__.py +8 -0
  239. parrot/loaders/abstract.py +1131 -0
  240. parrot/loaders/audio.py +199 -0
  241. parrot/loaders/basepdf.py +53 -0
  242. parrot/loaders/basevideo.py +1568 -0
  243. parrot/loaders/csv.py +409 -0
  244. parrot/loaders/docx.py +116 -0
  245. parrot/loaders/epubloader.py +316 -0
  246. parrot/loaders/excel.py +199 -0
  247. parrot/loaders/factory.py +55 -0
  248. parrot/loaders/files/__init__.py +0 -0
  249. parrot/loaders/files/abstract.py +39 -0
  250. parrot/loaders/files/html.py +26 -0
  251. parrot/loaders/files/text.py +63 -0
  252. parrot/loaders/html.py +152 -0
  253. parrot/loaders/markdown.py +442 -0
  254. parrot/loaders/pdf.py +373 -0
  255. parrot/loaders/pdfmark.py +320 -0
  256. parrot/loaders/pdftables.py +506 -0
  257. parrot/loaders/ppt.py +476 -0
  258. parrot/loaders/qa.py +63 -0
  259. parrot/loaders/splitters/__init__.py +10 -0
  260. parrot/loaders/splitters/base.py +138 -0
  261. parrot/loaders/splitters/md.py +228 -0
  262. parrot/loaders/splitters/token.py +143 -0
  263. parrot/loaders/txt.py +26 -0
  264. parrot/loaders/video.py +89 -0
  265. parrot/loaders/videolocal.py +218 -0
  266. parrot/loaders/videounderstanding.py +377 -0
  267. parrot/loaders/vimeo.py +167 -0
  268. parrot/loaders/web.py +599 -0
  269. parrot/loaders/youtube.py +504 -0
  270. parrot/manager/__init__.py +5 -0
  271. parrot/manager/manager.py +1030 -0
  272. parrot/mcp/__init__.py +28 -0
  273. parrot/mcp/adapter.py +105 -0
  274. parrot/mcp/cli.py +174 -0
  275. parrot/mcp/client.py +119 -0
  276. parrot/mcp/config.py +75 -0
  277. parrot/mcp/integration.py +842 -0
  278. parrot/mcp/oauth.py +933 -0
  279. parrot/mcp/server.py +225 -0
  280. parrot/mcp/transports/__init__.py +3 -0
  281. parrot/mcp/transports/base.py +279 -0
  282. parrot/mcp/transports/grpc_session.py +163 -0
  283. parrot/mcp/transports/http.py +312 -0
  284. parrot/mcp/transports/mcp.proto +108 -0
  285. parrot/mcp/transports/quic.py +1082 -0
  286. parrot/mcp/transports/sse.py +330 -0
  287. parrot/mcp/transports/stdio.py +309 -0
  288. parrot/mcp/transports/unix.py +395 -0
  289. parrot/mcp/transports/websocket.py +547 -0
  290. parrot/memory/__init__.py +16 -0
  291. parrot/memory/abstract.py +209 -0
  292. parrot/memory/agent.py +32 -0
  293. parrot/memory/cache.py +175 -0
  294. parrot/memory/core.py +555 -0
  295. parrot/memory/file.py +153 -0
  296. parrot/memory/mem.py +131 -0
  297. parrot/memory/redis.py +613 -0
  298. parrot/models/__init__.py +46 -0
  299. parrot/models/basic.py +118 -0
  300. parrot/models/compliance.py +208 -0
  301. parrot/models/crew.py +395 -0
  302. parrot/models/detections.py +654 -0
  303. parrot/models/generation.py +85 -0
  304. parrot/models/google.py +223 -0
  305. parrot/models/groq.py +23 -0
  306. parrot/models/openai.py +30 -0
  307. parrot/models/outputs.py +285 -0
  308. parrot/models/responses.py +938 -0
  309. parrot/notifications/__init__.py +743 -0
  310. parrot/openapi/__init__.py +3 -0
  311. parrot/openapi/components.yaml +641 -0
  312. parrot/openapi/config.py +322 -0
  313. parrot/outputs/__init__.py +32 -0
  314. parrot/outputs/formats/__init__.py +108 -0
  315. parrot/outputs/formats/altair.py +359 -0
  316. parrot/outputs/formats/application.py +122 -0
  317. parrot/outputs/formats/base.py +351 -0
  318. parrot/outputs/formats/bokeh.py +356 -0
  319. parrot/outputs/formats/card.py +424 -0
  320. parrot/outputs/formats/chart.py +436 -0
  321. parrot/outputs/formats/d3.py +255 -0
  322. parrot/outputs/formats/echarts.py +310 -0
  323. parrot/outputs/formats/generators/__init__.py +0 -0
  324. parrot/outputs/formats/generators/abstract.py +61 -0
  325. parrot/outputs/formats/generators/panel.py +145 -0
  326. parrot/outputs/formats/generators/streamlit.py +86 -0
  327. parrot/outputs/formats/generators/terminal.py +63 -0
  328. parrot/outputs/formats/holoviews.py +310 -0
  329. parrot/outputs/formats/html.py +147 -0
  330. parrot/outputs/formats/jinja2.py +46 -0
  331. parrot/outputs/formats/json.py +87 -0
  332. parrot/outputs/formats/map.py +933 -0
  333. parrot/outputs/formats/markdown.py +172 -0
  334. parrot/outputs/formats/matplotlib.py +237 -0
  335. parrot/outputs/formats/mixins/__init__.py +0 -0
  336. parrot/outputs/formats/mixins/emaps.py +855 -0
  337. parrot/outputs/formats/plotly.py +341 -0
  338. parrot/outputs/formats/seaborn.py +310 -0
  339. parrot/outputs/formats/table.py +397 -0
  340. parrot/outputs/formats/template_report.py +138 -0
  341. parrot/outputs/formats/yaml.py +125 -0
  342. parrot/outputs/formatter.py +152 -0
  343. parrot/outputs/templates/__init__.py +95 -0
  344. parrot/pipelines/__init__.py +0 -0
  345. parrot/pipelines/abstract.py +210 -0
  346. parrot/pipelines/detector.py +124 -0
  347. parrot/pipelines/models.py +90 -0
  348. parrot/pipelines/planogram.py +3002 -0
  349. parrot/pipelines/table.sql +97 -0
  350. parrot/plugins/__init__.py +106 -0
  351. parrot/plugins/importer.py +80 -0
  352. parrot/py.typed +0 -0
  353. parrot/registry/__init__.py +18 -0
  354. parrot/registry/registry.py +594 -0
  355. parrot/scheduler/__init__.py +1189 -0
  356. parrot/scheduler/models.py +60 -0
  357. parrot/security/__init__.py +16 -0
  358. parrot/security/prompt_injection.py +268 -0
  359. parrot/security/security_events.sql +25 -0
  360. parrot/services/__init__.py +1 -0
  361. parrot/services/mcp/__init__.py +8 -0
  362. parrot/services/mcp/config.py +13 -0
  363. parrot/services/mcp/server.py +295 -0
  364. parrot/services/o365_remote_auth.py +235 -0
  365. parrot/stores/__init__.py +7 -0
  366. parrot/stores/abstract.py +352 -0
  367. parrot/stores/arango.py +1090 -0
  368. parrot/stores/bigquery.py +1377 -0
  369. parrot/stores/cache.py +106 -0
  370. parrot/stores/empty.py +10 -0
  371. parrot/stores/faiss_store.py +1157 -0
  372. parrot/stores/kb/__init__.py +9 -0
  373. parrot/stores/kb/abstract.py +68 -0
  374. parrot/stores/kb/cache.py +165 -0
  375. parrot/stores/kb/doc.py +325 -0
  376. parrot/stores/kb/hierarchy.py +346 -0
  377. parrot/stores/kb/local.py +457 -0
  378. parrot/stores/kb/prompt.py +28 -0
  379. parrot/stores/kb/redis.py +659 -0
  380. parrot/stores/kb/store.py +115 -0
  381. parrot/stores/kb/user.py +374 -0
  382. parrot/stores/models.py +59 -0
  383. parrot/stores/pgvector.py +3 -0
  384. parrot/stores/postgres.py +2853 -0
  385. parrot/stores/utils/__init__.py +0 -0
  386. parrot/stores/utils/chunking.py +197 -0
  387. parrot/telemetry/__init__.py +3 -0
  388. parrot/telemetry/mixin.py +111 -0
  389. parrot/template/__init__.py +3 -0
  390. parrot/template/engine.py +259 -0
  391. parrot/tools/__init__.py +23 -0
  392. parrot/tools/abstract.py +644 -0
  393. parrot/tools/agent.py +363 -0
  394. parrot/tools/arangodbsearch.py +537 -0
  395. parrot/tools/arxiv_tool.py +188 -0
  396. parrot/tools/calculator/__init__.py +3 -0
  397. parrot/tools/calculator/operations/__init__.py +38 -0
  398. parrot/tools/calculator/operations/calculus.py +80 -0
  399. parrot/tools/calculator/operations/statistics.py +76 -0
  400. parrot/tools/calculator/tool.py +150 -0
  401. parrot/tools/cloudwatch.py +988 -0
  402. parrot/tools/codeinterpreter/__init__.py +127 -0
  403. parrot/tools/codeinterpreter/executor.py +371 -0
  404. parrot/tools/codeinterpreter/internals.py +473 -0
  405. parrot/tools/codeinterpreter/models.py +643 -0
  406. parrot/tools/codeinterpreter/prompts.py +224 -0
  407. parrot/tools/codeinterpreter/tool.py +664 -0
  408. parrot/tools/company_info/__init__.py +6 -0
  409. parrot/tools/company_info/tool.py +1138 -0
  410. parrot/tools/correlationanalysis.py +437 -0
  411. parrot/tools/database/abstract.py +286 -0
  412. parrot/tools/database/bq.py +115 -0
  413. parrot/tools/database/cache.py +284 -0
  414. parrot/tools/database/models.py +95 -0
  415. parrot/tools/database/pg.py +343 -0
  416. parrot/tools/databasequery.py +1159 -0
  417. parrot/tools/db.py +1800 -0
  418. parrot/tools/ddgo.py +370 -0
  419. parrot/tools/decorators.py +271 -0
  420. parrot/tools/dftohtml.py +282 -0
  421. parrot/tools/document.py +549 -0
  422. parrot/tools/ecs.py +819 -0
  423. parrot/tools/edareport.py +368 -0
  424. parrot/tools/elasticsearch.py +1049 -0
  425. parrot/tools/employees.py +462 -0
  426. parrot/tools/epson/__init__.py +96 -0
  427. parrot/tools/excel.py +683 -0
  428. parrot/tools/file/__init__.py +13 -0
  429. parrot/tools/file/abstract.py +76 -0
  430. parrot/tools/file/gcs.py +378 -0
  431. parrot/tools/file/local.py +284 -0
  432. parrot/tools/file/s3.py +511 -0
  433. parrot/tools/file/tmp.py +309 -0
  434. parrot/tools/file/tool.py +501 -0
  435. parrot/tools/file_reader.py +129 -0
  436. parrot/tools/flowtask/__init__.py +19 -0
  437. parrot/tools/flowtask/tool.py +761 -0
  438. parrot/tools/gittoolkit.py +508 -0
  439. parrot/tools/google/__init__.py +18 -0
  440. parrot/tools/google/base.py +169 -0
  441. parrot/tools/google/tools.py +1251 -0
  442. parrot/tools/googlelocation.py +5 -0
  443. parrot/tools/googleroutes.py +5 -0
  444. parrot/tools/googlesearch.py +5 -0
  445. parrot/tools/googlesitesearch.py +5 -0
  446. parrot/tools/googlevoice.py +2 -0
  447. parrot/tools/gvoice.py +695 -0
  448. parrot/tools/ibisworld/README.md +225 -0
  449. parrot/tools/ibisworld/__init__.py +11 -0
  450. parrot/tools/ibisworld/tool.py +366 -0
  451. parrot/tools/jiratoolkit.py +1718 -0
  452. parrot/tools/manager.py +1098 -0
  453. parrot/tools/math.py +152 -0
  454. parrot/tools/metadata.py +476 -0
  455. parrot/tools/msteams.py +1621 -0
  456. parrot/tools/msword.py +635 -0
  457. parrot/tools/multidb.py +580 -0
  458. parrot/tools/multistoresearch.py +369 -0
  459. parrot/tools/networkninja.py +167 -0
  460. parrot/tools/nextstop/__init__.py +4 -0
  461. parrot/tools/nextstop/base.py +286 -0
  462. parrot/tools/nextstop/employee.py +733 -0
  463. parrot/tools/nextstop/store.py +462 -0
  464. parrot/tools/notification.py +435 -0
  465. parrot/tools/o365/__init__.py +42 -0
  466. parrot/tools/o365/base.py +295 -0
  467. parrot/tools/o365/bundle.py +522 -0
  468. parrot/tools/o365/events.py +554 -0
  469. parrot/tools/o365/mail.py +992 -0
  470. parrot/tools/o365/onedrive.py +497 -0
  471. parrot/tools/o365/sharepoint.py +641 -0
  472. parrot/tools/openapi_toolkit.py +904 -0
  473. parrot/tools/openweather.py +527 -0
  474. parrot/tools/pdfprint.py +1001 -0
  475. parrot/tools/powerbi.py +518 -0
  476. parrot/tools/powerpoint.py +1113 -0
  477. parrot/tools/pricestool.py +146 -0
  478. parrot/tools/products/__init__.py +246 -0
  479. parrot/tools/prophet_tool.py +171 -0
  480. parrot/tools/pythonpandas.py +630 -0
  481. parrot/tools/pythonrepl.py +910 -0
  482. parrot/tools/qsource.py +436 -0
  483. parrot/tools/querytoolkit.py +395 -0
  484. parrot/tools/quickeda.py +827 -0
  485. parrot/tools/resttool.py +553 -0
  486. parrot/tools/retail/__init__.py +0 -0
  487. parrot/tools/retail/bby.py +528 -0
  488. parrot/tools/sandboxtool.py +703 -0
  489. parrot/tools/sassie/__init__.py +352 -0
  490. parrot/tools/scraping/__init__.py +7 -0
  491. parrot/tools/scraping/docs/select.md +466 -0
  492. parrot/tools/scraping/documentation.md +1278 -0
  493. parrot/tools/scraping/driver.py +436 -0
  494. parrot/tools/scraping/models.py +576 -0
  495. parrot/tools/scraping/options.py +85 -0
  496. parrot/tools/scraping/orchestrator.py +517 -0
  497. parrot/tools/scraping/readme.md +740 -0
  498. parrot/tools/scraping/tool.py +3115 -0
  499. parrot/tools/seasonaldetection.py +642 -0
  500. parrot/tools/shell_tool/__init__.py +5 -0
  501. parrot/tools/shell_tool/actions.py +408 -0
  502. parrot/tools/shell_tool/engine.py +155 -0
  503. parrot/tools/shell_tool/models.py +322 -0
  504. parrot/tools/shell_tool/tool.py +442 -0
  505. parrot/tools/site_search.py +214 -0
  506. parrot/tools/textfile.py +418 -0
  507. parrot/tools/think.py +378 -0
  508. parrot/tools/toolkit.py +298 -0
  509. parrot/tools/webapp_tool.py +187 -0
  510. parrot/tools/whatif.py +1279 -0
  511. parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
  512. parrot/tools/workday/__init__.py +6 -0
  513. parrot/tools/workday/models.py +1389 -0
  514. parrot/tools/workday/tool.py +1293 -0
  515. parrot/tools/yfinance_tool.py +306 -0
  516. parrot/tools/zipcode.py +217 -0
  517. parrot/utils/__init__.py +2 -0
  518. parrot/utils/helpers.py +73 -0
  519. parrot/utils/parsers/__init__.py +5 -0
  520. parrot/utils/parsers/toml.c +12078 -0
  521. parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
  522. parrot/utils/parsers/toml.pyx +21 -0
  523. parrot/utils/toml.py +11 -0
  524. parrot/utils/types.cpp +20936 -0
  525. parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
  526. parrot/utils/types.pyx +213 -0
  527. parrot/utils/uv.py +11 -0
  528. parrot/version.py +10 -0
  529. parrot/yaml-rs/Cargo.lock +350 -0
  530. parrot/yaml-rs/Cargo.toml +19 -0
  531. parrot/yaml-rs/pyproject.toml +19 -0
  532. parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
  533. parrot/yaml-rs/src/lib.rs +222 -0
  534. requirements/docker-compose.yml +24 -0
  535. requirements/requirements-dev.txt +21 -0
parrot/tools/gvoice.py ADDED
@@ -0,0 +1,695 @@
1
+ """
2
+ Google Text-to-Speech Tool migrated to use AbstractTool framework with async support.
3
+ """
4
+ import asyncio
5
+ import os
6
+ import re
7
+ from pathlib import Path
8
+ from typing import Any, Dict, Optional, Literal
9
+ from datetime import datetime
10
+ from xml.sax.saxutils import escape
11
+ import traceback
12
+ import aiofiles
13
+ import markdown
14
+ import bs4
15
+ from google.cloud import texttospeech_v1 as texttospeech
16
+ from google.oauth2 import service_account
17
+ from pydantic import BaseModel, Field, field_validator
18
+ from .abstract import AbstractTool
19
+
20
+
21
+ # Markdown cleaning utilities
22
+ MD_REPLACEMENTS = [
23
+ # inline code: `print("hi")` → print("hi")
24
+ (r"`([^`]*)`", r"\1"),
25
+ # bold / italic: **text** or *text* or _text_ → text
26
+ (r"\*\*([^*]+)\*\*", r"\1"),
27
+ (r"[_*]([^_*]+)[_*]", r"\1"),
28
+ # strikethrough: ~~text~~
29
+ (r"~~([^~]+)~~", r"\1"),
30
+ # links: [label](url) → label
31
+ (r"\[([^\]]+)\]\([^)]+\)", r"\1"),
32
+ ]
33
+
34
+
35
+ def strip_markdown(text: str) -> str:
36
+ """Remove the most common inline Markdown markers."""
37
+ for pattern, repl in MD_REPLACEMENTS:
38
+ text = re.sub(pattern, repl, text)
39
+ return text
40
+
41
+
42
+ def markdown_to_plain(md: str) -> str:
43
+ """Convert Markdown to plain text via HTML parsing."""
44
+ html = markdown.markdown(md, extensions=["extra", "smarty"])
45
+ return ''.join(bs4.BeautifulSoup(html, "html.parser").stripped_strings)
46
+
47
+
48
+ class GoogleTTSArgs(BaseModel):
49
+ """Arguments schema for GoogleTTSTool."""
50
+
51
+ text: str = Field(
52
+ ...,
53
+ description="The text content (plaintext or Markdown) to convert to speech"
54
+ )
55
+ voice_model: Optional[str] = Field(
56
+ None,
57
+ description="Specific Google voice model name (e.g., 'en-US-Neural2-F'). If None, selects based on language and gender"
58
+ )
59
+ voice_gender: Literal["MALE", "FEMALE"] = Field(
60
+ "FEMALE",
61
+ description="Voice gender preference when voice_model is not specified"
62
+ )
63
+ language_code: str = Field(
64
+ "en-US",
65
+ description="BCP-47 language code (e.g., 'en-US', 'es-ES', 'fr-FR')"
66
+ )
67
+ output_format: Literal["OGG_OPUS", "MP3", "LINEAR16", "MULAW", "ALAW", "PCM"] = Field(
68
+ "OGG_OPUS",
69
+ description="Audio output format"
70
+ )
71
+ file_prefix: str = Field(
72
+ "podcast",
73
+ description="Prefix for the output filename (timestamp and extension added automatically)"
74
+ )
75
+ speaking_rate: float = Field(
76
+ 1.0,
77
+ description="Speaking rate (0.25 to 4.0, where 1.0 is normal speed)",
78
+ ge=0.25,
79
+ le=4.0
80
+ )
81
+ pitch: float = Field(
82
+ 0.0,
83
+ description="Voice pitch (-20.0 to 20.0 semitones, where 0.0 is normal)",
84
+ ge=-20.0,
85
+ le=20.0
86
+ )
87
+ use_ssml: bool = Field(
88
+ True,
89
+ description="Whether to convert Markdown/text to SSML for better speech synthesis"
90
+ )
91
+
92
+ @field_validator('text')
93
+ @classmethod
94
+ def validate_text(cls, v):
95
+ if not v or not v.strip():
96
+ raise ValueError("Text content cannot be empty")
97
+ return v
98
+
99
+ @field_validator('language_code')
100
+ @classmethod
101
+ def validate_language_code(cls, v):
102
+ # Basic validation for BCP-47 format
103
+ if not re.match(r'^[a-z]{2,3}(-[A-Z]{2})?$', v):
104
+ raise ValueError("Language code must be in BCP-47 format (e.g., 'en-US', 'es-ES')")
105
+ return v
106
+
107
+
108
+ class GoogleVoiceTool(AbstractTool):
109
+ """
110
+ Tool for generating speech audio from text using Google Cloud Text-to-Speech.
111
+
112
+ This tool converts text content (including Markdown) into high-quality speech audio
113
+ using Google's neural voice models. It supports multiple languages, voice customization,
114
+ and various audio output formats.
115
+
116
+ Features:
117
+ - Automatic Markdown to SSML conversion for natural speech
118
+ - Multiple voice models and languages
119
+ - Configurable speech parameters (rate, pitch)
120
+ - Various audio output formats (OGG, MP3, WAV, etc.)
121
+ - Async processing for better performance
122
+ - Comprehensive error handling and logging
123
+ """
124
+
125
+ name = "google_tts_service"
126
+ description = (
127
+ "Generate speech audio from text using Google Cloud Text-to-Speech. "
128
+ "Supports multiple languages, voice models, and output formats. "
129
+ "Can process both plain text and Markdown content with natural speech synthesis."
130
+ )
131
+ args_schema = GoogleTTSArgs
132
+
133
+ # Voice model mappings by language and gender
134
+ VOICE_MODELS = {
135
+ "en-US": {
136
+ "MALE": "en-US-Neural2-D",
137
+ "FEMALE": "en-US-Neural2-F"
138
+ },
139
+ "es-ES": {
140
+ "MALE": "es-ES-Polyglot-1",
141
+ "FEMALE": "es-ES-Neural2-H"
142
+ },
143
+ "fr-FR": {
144
+ "MALE": "fr-FR-Neural2-G",
145
+ "FEMALE": "fr-FR-Neural2-F"
146
+ },
147
+ "de-DE": {
148
+ "MALE": "de-DE-Neural2-G",
149
+ "FEMALE": "de-DE-Neural2-F"
150
+ },
151
+ "cmn-CN": {
152
+ "MALE": "cmn-CN-Standard-B",
153
+ "FEMALE": "cmn-CN-Standard-D"
154
+ },
155
+ "zh-CN": {
156
+ "MALE": "cmn-CN-Standard-B",
157
+ "FEMALE": "cmn-CN-Standard-D"
158
+ },
159
+ "ja-JP": {
160
+ "MALE": "ja-JP-Neural2-C",
161
+ "FEMALE": "ja-JP-Neural2-B"
162
+ },
163
+ "ko-KR": {
164
+ "MALE": "ko-KR-Neural2-C",
165
+ "FEMALE": "ko-KR-Neural2-A"
166
+ },
167
+ "pt-BR": {
168
+ "MALE": "pt-BR-Neural2-B",
169
+ "FEMALE": "pt-BR-Neural2-A"
170
+ },
171
+ "it-IT": {
172
+ "MALE": "it-IT-Neural2-C",
173
+ "FEMALE": "it-IT-Neural2-A"
174
+ }
175
+ }
176
+
177
+ # Audio format mappings
178
+ FORMAT_MAPPING = {
179
+ "OGG_OPUS": (texttospeech.AudioEncoding.OGG_OPUS, "ogg"),
180
+ "MP3": (texttospeech.AudioEncoding.MP3, "mp3"),
181
+ "LINEAR16": (texttospeech.AudioEncoding.LINEAR16, "wav"),
182
+ "MULAW": (texttospeech.AudioEncoding.MULAW, "wav"),
183
+ "ALAW": (texttospeech.AudioEncoding.ALAW, "wav"),
184
+ "PCM": (texttospeech.AudioEncoding.PCM, "pcm")
185
+ }
186
+
187
+ def __init__(
188
+ self,
189
+ credentials_path: Optional[str] = None,
190
+ default_voice_model: str = "en-US-Neural2-F",
191
+ default_language: str = "en-US",
192
+ default_format: str = "OGG_OPUS",
193
+ use_long_audio_synthesis: bool = False,
194
+ **kwargs
195
+ ):
196
+ """
197
+ Initialize the Google TTS Tool.
198
+
199
+ Args:
200
+ credentials_path: Path to Google Cloud service account JSON file
201
+ default_voice_model: Default voice model to use
202
+ default_language: Default language code
203
+ default_format: Default audio output format
204
+ use_long_audio_synthesis: Whether to use long audio synthesis for texts >5000 chars
205
+ **kwargs: Additional arguments for AbstractTool
206
+ """
207
+ super().__init__(**kwargs)
208
+
209
+ # Set up credentials
210
+ self.credentials_path = credentials_path or os.getenv('GOOGLE_APPLICATION_CREDENTIALS')
211
+ if not self.credentials_path:
212
+ # Try common paths
213
+ possible_paths = [
214
+ Path.cwd() / "credentials" / "google-tts.json",
215
+ Path.cwd() / "env" / "google" / "tts-service.json",
216
+ Path(__file__).parent.parent / "credentials" / "google-tts.json"
217
+ ]
218
+
219
+ for path in possible_paths:
220
+ if path.exists():
221
+ self.credentials_path = str(path)
222
+ break
223
+
224
+ if not self.credentials_path or not Path(self.credentials_path).exists():
225
+ raise ValueError(
226
+ "Google TTS credentials not found. Please provide credentials_path or set "
227
+ "GOOGLE_APPLICATION_CREDENTIALS environment variable."
228
+ )
229
+
230
+ # Configuration
231
+ self.default_voice_model = default_voice_model
232
+ self.default_language = default_language
233
+ self.default_format = default_format
234
+ self.use_long_audio_synthesis = use_long_audio_synthesis
235
+
236
+ # Initialize credentials
237
+ try:
238
+ self.credentials = service_account.Credentials.from_service_account_file(
239
+ self.credentials_path
240
+ )
241
+ self.logger.info(f"Google TTS tool initialized with credentials from: {self.credentials_path}")
242
+ except Exception as e:
243
+ raise ValueError(f"Failed to load Google TTS credentials: {e}")
244
+
245
+ def _default_output_dir(self) -> Path:
246
+ """Get the default output directory for TTS audio files."""
247
+ return self.static_dir / "audio" / "tts"
248
+
249
+ def _is_markdown(self, text: str) -> bool:
250
+ """Determine if the text appears to be Markdown formatted."""
251
+ if not text or not isinstance(text, str):
252
+ return False
253
+
254
+ text = text.strip()
255
+ if not text:
256
+ return False
257
+
258
+ # Check first character for Markdown markers
259
+ first_char = text[0]
260
+ if first_char in "#*_>`-":
261
+ return True
262
+
263
+ # Check if first character is a digit (for numbered lists)
264
+ if first_char.isdigit() and re.match(r'^\d+\.', text):
265
+ return True
266
+
267
+ # Check for common Markdown patterns
268
+ markdown_patterns = [
269
+ r"#{1,6}\s+", # Headers
270
+ r"\*\*.*?\*\*", # Bold
271
+ r"__.*?__", # Bold alternative
272
+ r"\*.*?\*", # Italic
273
+ r"_.*?_", # Italic alternative
274
+ r"`.*?`", # Inline code
275
+ r"\[.*?\]\(.*?\)", # Links
276
+ r"^\s*[\*\-\+]\s+", # Unordered lists
277
+ r"^\s*\d+\.\s+", # Ordered lists
278
+ r"```.*?```", # Code blocks
279
+ r"^\s*>\s+", # Blockquotes
280
+ ]
281
+
282
+ for pattern in markdown_patterns:
283
+ if re.search(pattern, text, re.MULTILINE | re.DOTALL):
284
+ return True
285
+
286
+ return False
287
+
288
+ def _text_to_ssml(self, text: str) -> str:
289
+ """Convert plain text to SSML."""
290
+ escaped_text = escape(text)
291
+ return f"<speak><p>{escaped_text}</p></speak>"
292
+
293
+ def _markdown_to_ssml(self, markdown_text: str) -> str:
294
+ """Convert Markdown text to SSML for natural speech synthesis."""
295
+ # Handle code block prefixes
296
+ if markdown_text.startswith("```text"):
297
+ markdown_text = markdown_text[len("```text"):].strip()
298
+
299
+ ssml = "<speak>"
300
+ lines = markdown_text.split('\n')
301
+ in_code_block = False
302
+
303
+ for line in lines:
304
+ line = line.strip()
305
+
306
+ # Handle code blocks
307
+ if line.startswith("```"):
308
+ in_code_block = not in_code_block
309
+ if in_code_block:
310
+ ssml += '<prosody rate="x-slow"><p>'
311
+ else:
312
+ ssml += '</p></prosody>'
313
+ continue
314
+
315
+ if in_code_block:
316
+ # Speak code slowly with pauses
317
+ ssml += escape(line) + '<break time="200ms"/>'
318
+ continue
319
+
320
+ # Handle ellipses for dramatic pauses
321
+ if line == "...":
322
+ ssml += '<break time="1s"/>'
323
+ continue
324
+
325
+ # Handle Markdown headings
326
+ heading_match = re.match(r"^(#+)\s+(.*)", line)
327
+ if heading_match:
328
+ heading_level = len(heading_match.group(1))
329
+ heading_text = heading_match.group(2).strip()
330
+
331
+ # Vary emphasis based on heading level
332
+ if heading_level == 1:
333
+ ssml += f'<p><emphasis level="strong"><prosody rate="slow">{escape(heading_text)}</prosody></emphasis></p><break time="500ms"/>'
334
+ elif heading_level == 2:
335
+ ssml += f'<p><emphasis level="moderate">{escape(heading_text)}</emphasis></p><break time="300ms"/>'
336
+ else:
337
+ ssml += f'<p><emphasis level="reduced">{escape(heading_text)}</emphasis></p><break time="200ms"/>'
338
+ continue
339
+
340
+ # Handle regular content
341
+ if line:
342
+ # Clean Markdown formatting for speech
343
+ clean_text = strip_markdown(line)
344
+ ssml += f'<p>{escape(clean_text)}</p>'
345
+
346
+ ssml += "</speak>"
347
+ return ssml
348
+
349
+ def _select_voice_model(self, voice_model: Optional[str], language_code: str, voice_gender: str) -> str:
350
+ """Select appropriate voice model based on parameters."""
351
+ # If specific voice model provided, use it
352
+ if voice_model:
353
+ return voice_model
354
+
355
+ # Select voice based on language and gender
356
+ if language_code in self.VOICE_MODELS:
357
+ return self.VOICE_MODELS[language_code].get(voice_gender, self.default_voice_model)
358
+
359
+ # Fallback to default
360
+ self.logger.warning(f"No voice model found for {language_code}:{voice_gender}, using default")
361
+ return self.default_voice_model
362
+
363
+ def _get_audio_config(self, output_format: str, speaking_rate: float, pitch: float) -> tuple:
364
+ """Get audio encoding configuration and file extension."""
365
+ if output_format not in self.FORMAT_MAPPING:
366
+ available_formats = ', '.join(self.FORMAT_MAPPING.keys())
367
+ raise ValueError(f"Unsupported output format: {output_format}. Available: {available_formats}")
368
+
369
+ encoding, extension = self.FORMAT_MAPPING[output_format]
370
+
371
+ audio_config = texttospeech.AudioConfig(
372
+ audio_encoding=encoding,
373
+ speaking_rate=speaking_rate,
374
+ pitch=pitch
375
+ )
376
+
377
+ return audio_config, extension
378
+
379
+ async def _synthesize_speech_short(
380
+ self,
381
+ text_input: str,
382
+ voice_model: str,
383
+ language_code: str,
384
+ audio_config: texttospeech.AudioConfig,
385
+ use_ssml: bool
386
+ ) -> bytes:
387
+ """Synthesize speech for shorter texts using standard API."""
388
+ try:
389
+ # Create async client
390
+ client = texttospeech.TextToSpeechAsyncClient(credentials=self.credentials)
391
+
392
+ # Prepare synthesis input
393
+ if use_ssml:
394
+ synthesis_input = texttospeech.SynthesisInput(ssml=text_input)
395
+ else:
396
+ synthesis_input = texttospeech.SynthesisInput(text=text_input)
397
+
398
+ # Configure voice
399
+ voice = texttospeech.VoiceSelectionParams(
400
+ language_code=language_code,
401
+ name=voice_model
402
+ )
403
+
404
+ # Make async request
405
+ response = await client.synthesize_speech(
406
+ input=synthesis_input,
407
+ voice=voice,
408
+ audio_config=audio_config
409
+ )
410
+
411
+ return response.audio_content
412
+
413
+ except Exception as e:
414
+ raise Exception(f"Speech synthesis failed: {str(e)}")
415
+
416
+ async def _synthesize_speech_long(
417
+ self,
418
+ text_input: str,
419
+ voice_model: str,
420
+ language_code: str,
421
+ audio_config: texttospeech.AudioConfig,
422
+ output_gcs_uri: str,
423
+ use_ssml: bool
424
+ ) -> str:
425
+ """Synthesize speech for longer texts using long audio synthesis API."""
426
+ try:
427
+ # Create long audio synthesis client
428
+ client = texttospeech.TextToSpeechLongAudioSynthesizeAsyncClient(
429
+ credentials=self.credentials
430
+ )
431
+
432
+ # Prepare synthesis input
433
+ if use_ssml:
434
+ synthesis_input = texttospeech.SynthesisInput(ssml=text_input)
435
+ else:
436
+ synthesis_input = texttospeech.SynthesisInput(text=text_input)
437
+
438
+ # Configure voice
439
+ voice = texttospeech.VoiceSelectionParams(
440
+ language_code=language_code,
441
+ name=voice_model
442
+ )
443
+
444
+ # Create request
445
+ request = texttospeech.SynthesizeLongAudioRequest(
446
+ input=synthesis_input,
447
+ audio_config=audio_config,
448
+ output_gcs_uri=output_gcs_uri,
449
+ voice=voice
450
+ )
451
+
452
+ # Make async request
453
+ operation = await client.synthesize_long_audio(request=request)
454
+
455
+ self.logger.info("Waiting for long audio synthesis to complete...")
456
+ response = await operation.result()
457
+
458
+ return output_gcs_uri
459
+
460
+ except Exception as e:
461
+ raise Exception(f"Long audio synthesis failed: {str(e)}")
462
+
463
+ async def _execute(
464
+ self,
465
+ text: str,
466
+ voice_model: Optional[str] = None,
467
+ voice_gender: str = "FEMALE",
468
+ language_code: str = "en-US",
469
+ output_format: str = "OGG_OPUS",
470
+ file_prefix: str = "podcast",
471
+ speaking_rate: float = 1.0,
472
+ pitch: float = 0.0,
473
+ use_ssml: bool = True,
474
+ **kwargs
475
+ ) -> Dict[str, Any]:
476
+ """
477
+ Execute text-to-speech conversion (AbstractTool interface).
478
+
479
+ Args:
480
+ text: Text content to convert to speech
481
+ voice_model: Specific voice model or None for auto-selection
482
+ voice_gender: Voice gender preference
483
+ language_code: Language code (BCP-47 format)
484
+ output_format: Audio output format
485
+ file_prefix: Output filename prefix
486
+ speaking_rate: Speech rate (0.25-4.0)
487
+ pitch: Voice pitch (-20.0-20.0)
488
+ use_ssml: Whether to use SSML processing
489
+ **kwargs: Additional arguments
490
+
491
+ Returns:
492
+ Dictionary with synthesis results and file information
493
+ """
494
+ try:
495
+ self.logger.info(f"Starting TTS synthesis: {len(text)} characters, {language_code}, {voice_gender}")
496
+
497
+ # Select voice model
498
+ selected_voice = self._select_voice_model(voice_model, language_code, voice_gender)
499
+
500
+ # Get audio configuration
501
+ audio_config, file_extension = self._get_audio_config(output_format, speaking_rate, pitch)
502
+
503
+ # Process text based on type and SSML preference
504
+ if use_ssml:
505
+ if self._is_markdown(text):
506
+ self.logger.info("Converting Markdown to SSML")
507
+ processed_text = self._markdown_to_ssml(text)
508
+ else:
509
+ self.logger.info("Converting plain text to SSML")
510
+ processed_text = self._text_to_ssml(text)
511
+ else:
512
+ processed_text = text
513
+
514
+ # Generate output filename
515
+ output_filename = self.generate_filename(
516
+ prefix=file_prefix,
517
+ extension=file_extension,
518
+ include_timestamp=True
519
+ )
520
+
521
+ # Ensure output directory exists
522
+ self.output_dir.mkdir(parents=True, exist_ok=True)
523
+ output_path = self.output_dir / output_filename
524
+ output_path = self.validate_output_path(output_path)
525
+
526
+ # Determine synthesis method based on text length
527
+ char_count = len(processed_text)
528
+ use_long_synthesis = self.use_long_audio_synthesis and char_count > 5000
529
+
530
+ if use_long_synthesis:
531
+ self.logger.info(f"Using long audio synthesis for {char_count} characters")
532
+ # Note: Long synthesis requires Google Cloud Storage
533
+ # For now, fall back to standard synthesis
534
+ self.logger.warning("Long audio synthesis requires GCS setup, falling back to standard synthesis")
535
+ use_long_synthesis = False
536
+
537
+ # Synthesize speech
538
+ self.logger.info(f"Synthesizing speech with voice: {selected_voice}")
539
+
540
+ audio_content = await self._synthesize_speech_short(
541
+ processed_text,
542
+ selected_voice,
543
+ language_code,
544
+ audio_config,
545
+ use_ssml
546
+ )
547
+
548
+ # Save audio file
549
+ self.logger.info(f"Saving audio to: {output_path}")
550
+ async with aiofiles.open(output_path, 'wb') as audio_file:
551
+ await audio_file.write(audio_content)
552
+
553
+ # Generate URLs
554
+ file_url = self.to_static_url(output_path)
555
+ relative_url = self.relative_url(file_url)
556
+
557
+ # Calculate file statistics
558
+ file_size = output_path.stat().st_size
559
+ duration_estimate = len(text.split()) / 2.5 # Rough estimate: ~150 WPM
560
+
561
+ result = {
562
+ "filename": output_filename,
563
+ "file_path": str(output_path),
564
+ "file_url": file_url,
565
+ "relative_url": relative_url,
566
+ "file_size": file_size,
567
+ "file_size_mb": round(file_size / (1024 * 1024), 2),
568
+ "synthesis_info": {
569
+ "voice_model": selected_voice,
570
+ "language_code": language_code,
571
+ "voice_gender": voice_gender,
572
+ "output_format": output_format,
573
+ "speaking_rate": speaking_rate,
574
+ "pitch": pitch,
575
+ "used_ssml": use_ssml,
576
+ "was_markdown": use_ssml and self._is_markdown(text),
577
+ "character_count": len(text),
578
+ "processed_character_count": len(processed_text),
579
+ "estimated_duration_seconds": round(duration_estimate * 60, 1)
580
+ },
581
+ "generation_info": {
582
+ "timestamp": datetime.now().isoformat(),
583
+ "synthesis_method": "long" if use_long_synthesis else "standard",
584
+ "output_dir": str(self.output_dir)
585
+ }
586
+ }
587
+
588
+ self.logger.info(f"TTS synthesis completed: {file_size} bytes, ~{duration_estimate:.1f} minutes")
589
+ return result
590
+
591
+ except Exception as e:
592
+ self.logger.error(f"Error in TTS synthesis: {e}")
593
+ self.logger.error(traceback.format_exc())
594
+ raise
595
+
596
+ def execute_sync(
597
+ self,
598
+ text: str,
599
+ voice_model: Optional[str] = None,
600
+ voice_gender: str = "FEMALE",
601
+ language_code: str = "en-US",
602
+ output_format: str = "OGG_OPUS",
603
+ file_prefix: str = "podcast",
604
+ speaking_rate: float = 1.0,
605
+ pitch: float = 0.0,
606
+ use_ssml: bool = True
607
+ ) -> Dict[str, Any]:
608
+ """
609
+ Execute TTS synthesis synchronously.
610
+
611
+ Args:
612
+ text: Text content to convert to speech
613
+ voice_model: Specific voice model
614
+ voice_gender: Voice gender preference
615
+ language_code: Language code
616
+ output_format: Audio output format
617
+ file_prefix: Output filename prefix
618
+ speaking_rate: Speech rate
619
+ pitch: Voice pitch
620
+ use_ssml: Whether to use SSML
621
+
622
+ Returns:
623
+ Dictionary with synthesis results
624
+ """
625
+ try:
626
+ loop = asyncio.get_running_loop()
627
+ task = loop.create_task(self.execute(
628
+ text=text,
629
+ voice_model=voice_model,
630
+ voice_gender=voice_gender,
631
+ language_code=language_code,
632
+ output_format=output_format,
633
+ file_prefix=file_prefix,
634
+ speaking_rate=speaking_rate,
635
+ pitch=pitch,
636
+ use_ssml=use_ssml
637
+ ))
638
+ return task
639
+ except RuntimeError:
640
+ return asyncio.run(self.execute(
641
+ text=text,
642
+ voice_model=voice_model,
643
+ voice_gender=voice_gender,
644
+ language_code=language_code,
645
+ output_format=output_format,
646
+ file_prefix=file_prefix,
647
+ speaking_rate=speaking_rate,
648
+ pitch=pitch,
649
+ use_ssml=use_ssml
650
+ ))
651
+
652
+ def get_available_voices(self, language_code: Optional[str] = None) -> Dict[str, Any]:
653
+ """Get available voice models for a language or all languages."""
654
+ if language_code:
655
+ return self.VOICE_MODELS.get(language_code, {})
656
+ return self.VOICE_MODELS
657
+
658
+ def get_supported_formats(self) -> Dict[str, str]:
659
+ """Get supported audio output formats."""
660
+ return {fmt: ext for fmt, (_, ext) in self.FORMAT_MAPPING.items()}
661
+
662
+ def preview_ssml(self, text: str) -> str:
663
+ """Preview how text would be converted to SSML."""
664
+ try:
665
+ if self._is_markdown(text):
666
+ return self._markdown_to_ssml(text)
667
+ else:
668
+ return self._text_to_ssml(text)
669
+ except Exception as e:
670
+ self.logger.error(f"Error generating SSML preview: {e}")
671
+ return f"<speak><p>Error generating SSML: {e}</p></speak>"
672
+
673
+ def estimate_cost(self, text: str, use_ssml: bool = True) -> Dict[str, Any]:
674
+ """Estimate the cost for TTS synthesis (rough calculation)."""
675
+ # Process text to get accurate character count
676
+ if use_ssml:
677
+ if self._is_markdown(text):
678
+ processed_text = self._markdown_to_ssml(text)
679
+ else:
680
+ processed_text = self._text_to_ssml(text)
681
+ else:
682
+ processed_text = text
683
+
684
+ char_count = len(processed_text)
685
+
686
+ # Google TTS pricing (as of 2024): $4 per 1M characters for Neural2 voices
687
+ cost_per_million_chars = 4.0
688
+ estimated_cost = (char_count / 1_000_000) * cost_per_million_chars
689
+
690
+ return {
691
+ "original_characters": len(text),
692
+ "processed_characters": char_count,
693
+ "estimated_cost_usd": round(estimated_cost, 4),
694
+ "cost_breakdown": f"${cost_per_million_chars}/1M characters for Neural2 voices"
695
+ }