iatoolkit 2.3.4__tar.gz → 2.4.0__tar.gz

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 (232) hide show
  1. {iatoolkit-2.3.4/src/iatoolkit.egg-info → iatoolkit-2.4.0}/PKG-INFO +1 -1
  2. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/__init__.py +1 -1
  3. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/config/llm_capabilities.yaml +18 -0
  4. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +212 -27
  5. iatoolkit-2.4.0/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +62 -0
  6. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/llm_proxy.py +51 -3
  7. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/configuration_service.py +51 -0
  8. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/query_service.py +16 -2
  9. {iatoolkit-2.3.4 → iatoolkit-2.4.0/src/iatoolkit.egg-info}/PKG-INFO +1 -1
  10. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
  11. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/LICENSE +0 -0
  12. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/LICENSE_COMMUNITY.md +0 -0
  13. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/pyproject.toml +0 -0
  14. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/readme.md +0 -0
  15. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/requirements.txt +0 -0
  16. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/setup.cfg +0 -0
  17. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/base_company.py +0 -0
  18. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/cli_commands.py +0 -0
  19. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/__init__.py +0 -0
  20. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/exceptions.py +0 -0
  21. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/interfaces/__init__.py +0 -0
  22. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
  23. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
  24. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
  25. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/interfaces/memory_lint_trigger.py +0 -0
  26. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
  27. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
  28. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
  29. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/model_registry.py +0 -0
  30. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/routes.py +0 -0
  31. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/secret_resolver.py +0 -0
  32. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/session_manager.py +0 -0
  33. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/common/util.py +0 -0
  34. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/company_registry.py +0 -0
  35. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/config/__init__.py +0 -0
  36. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
  37. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
  38. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/core.py +0 -0
  39. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/__init__.py +0 -0
  40. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
  41. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/call_service.py +0 -0
  42. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  43. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  44. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  45. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  46. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  47. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  48. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  49. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/google_auth_client.py +0 -0
  50. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
  51. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
  52. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/inference_handler.py +0 -0
  53. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
  54. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
  55. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
  56. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
  57. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
  58. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
  59. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/llm_response.py +0 -0
  60. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  61. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/locales/en.yaml +0 -0
  62. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/locales/es.yaml +0 -0
  63. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/__init__.py +0 -0
  64. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/api_key_repo.py +0 -0
  65. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/database_manager.py +0 -0
  66. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/document_repo.py +0 -0
  67. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
  68. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
  69. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  70. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/memory_repo.py +0 -0
  71. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/models.py +0 -0
  72. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
  73. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
  74. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
  75. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
  76. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/repositories/vs_repo.py +0 -0
  77. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/__init__.py +0 -0
  78. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/api_key_service.py +0 -0
  79. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/attachment_policy_service.py +0 -0
  80. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/auth_service.py +0 -0
  81. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/benchmark_service.py +0 -0
  82. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/branding_service.py +0 -0
  83. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/company_context_service.py +0 -0
  84. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/context_builder_service.py +0 -0
  85. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/dispatcher_service.py +0 -0
  86. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/embedding_service.py +0 -0
  87. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/excel_service.py +0 -0
  88. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/file_processor_service.py +0 -0
  89. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/history_manager_service.py +0 -0
  90. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/http_tool_service.py +0 -0
  91. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/i18n_service.py +0 -0
  92. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/inference_service.py +0 -0
  93. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/jwt_service.py +0 -0
  94. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/knowledge_base_service.py +0 -0
  95. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/language_service.py +0 -0
  96. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/license_service.py +0 -0
  97. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/llm_client_service.py +0 -0
  98. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/mail_service.py +0 -0
  99. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/memory_compiler_service.py +0 -0
  100. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/memory_lint_service.py +0 -0
  101. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
  102. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/memory_service.py +0 -0
  103. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/memory_wiki_service.py +0 -0
  104. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
  105. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/noop_memory_lint_trigger.py +0 -0
  106. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/__init__.py +0 -0
  107. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/contracts.py +0 -0
  108. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
  109. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
  110. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
  111. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
  112. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
  113. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
  114. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
  115. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
  116. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/parsers/validator.py +0 -0
  117. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/pdf_service.py +0 -0
  118. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/profile_service.py +0 -0
  119. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/prompt_resource_service.py +0 -0
  120. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/prompt_service.py +0 -0
  121. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
  122. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/sql_dataset_service.py +0 -0
  123. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/sql_service.py +0 -0
  124. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/sql_source_service.py +0 -0
  125. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/storage_service.py +0 -0
  126. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/structured_output_service.py +0 -0
  127. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
  128. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/system_tools.py +0 -0
  129. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/tool_service.py +0 -0
  130. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/user_feedback_service.py +0 -0
  131. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
  132. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/visual_kb_service.py +0 -0
  133. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/visual_tool_service.py +0 -0
  134. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/warmup_service.py +0 -0
  135. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/web_search/__init__.py +0 -0
  136. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
  137. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
  138. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
  139. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/services/web_search_service.py +0 -0
  140. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  141. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
  142. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
  143. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
  144. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  145. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_help_content.js +0 -0
  146. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_history_button.js +0 -0
  147. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
  148. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_main.js +0 -0
  149. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
  150. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
  151. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
  152. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
  153. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
  154. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
  155. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  156. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/styles/chat_public.css +0 -0
  157. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/styles/documents.css +0 -0
  158. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/styles/landing_page.css +0 -0
  159. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
  160. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
  161. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/__init__.py +0 -0
  162. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/chat_state_rules.prompt +0 -0
  163. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/chat_user_profile.prompt +0 -0
  164. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/core_identity.prompt +0 -0
  165. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/email_output.prompt +0 -0
  166. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/file_download_output.prompt +0 -0
  167. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
  168. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/html_structures.prompt +0 -0
  169. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/links_documents.prompt +0 -0
  170. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/memory_usage.prompt +0 -0
  171. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/multimodal_basics.prompt +0 -0
  172. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/output_basics.prompt +0 -0
  173. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/presentation_formatting.prompt +0 -0
  174. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/query_main.prompt +0 -0
  175. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/sql_aggregation_scope.prompt +0 -0
  176. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/sql_casting.prompt +0 -0
  177. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/sql_core.prompt +0 -0
  178. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/sql_jsonb.prompt +0 -0
  179. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
  180. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/system_prompts/tool_html_passthrough.prompt +0 -0
  181. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/_company_header.html +0 -0
  182. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/_login_widget.html +0 -0
  183. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/base.html +0 -0
  184. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/change_password.html +0 -0
  185. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/chat.html +0 -0
  186. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
  187. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/chat_modals.html +0 -0
  188. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/error.html +0 -0
  189. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/forgot_password.html +0 -0
  190. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/home_hosted_default.html +0 -0
  191. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
  192. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  193. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/pdf/base.html +0 -0
  194. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/pdf/letter.html +0 -0
  195. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/pdf/report.html +0 -0
  196. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/pdf/simple.html +0 -0
  197. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/templates/signup.html +0 -0
  198. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/__init__.py +0 -0
  199. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/api_key_api_view.py +0 -0
  200. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/base_login_view.py +0 -0
  201. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/categories_api_view.py +0 -0
  202. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/change_password_view.py +0 -0
  203. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
  204. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/chat_view.py +0 -0
  205. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/configuration_api_view.py +0 -0
  206. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/connectors_api_view.py +0 -0
  207. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/embedding_api_view.py +0 -0
  208. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/forgot_password_view.py +0 -0
  209. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/help_content_api_view.py +0 -0
  210. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/history_api_view.py +0 -0
  211. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/home_view.py +0 -0
  212. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
  213. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
  214. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/load_document_api_view.py +0 -0
  215. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/login_view.py +0 -0
  216. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/logout_api_view.py +0 -0
  217. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/memory_api_view.py +0 -0
  218. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/profile_api_view.py +0 -0
  219. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/prompt_api_view.py +0 -0
  220. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
  221. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
  222. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/rag_api_view.py +0 -0
  223. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/root_redirect_view.py +0 -0
  224. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/signup_view.py +0 -0
  225. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/static_page_view.py +0 -0
  226. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/tool_api_view.py +0 -0
  227. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
  228. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/users_api_view.py +0 -0
  229. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit/views/verify_user_view.py +0 -0
  230. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  231. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit.egg-info/requires.txt +0 -0
  232. {iatoolkit-2.3.4 → iatoolkit-2.4.0}/src/iatoolkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 2.3.4
3
+ Version: 2.4.0
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- __version__ = "2.3.4"
6
+ __version__ = "2.4.0"
7
7
 
8
8
  # Expose main classes and functions at the top level of the package
9
9
 
@@ -58,6 +58,24 @@ openai_compatible:
58
58
  max_file_size_mb: 0
59
59
  max_files_per_request: 0
60
60
 
61
+ openrouter:
62
+ supports_native_files: true
63
+ supports_native_images: true
64
+ supported_mime_types:
65
+ - application/pdf
66
+ - text/plain
67
+ - text/csv
68
+ - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
69
+ - application/vnd.openxmlformats-officedocument.wordprocessingml.document
70
+ - application/vnd.openxmlformats-officedocument.presentationml.presentation
71
+ - application/json
72
+ - application/xml
73
+ - text/xml
74
+ preferred_native_mime_types:
75
+ - application/pdf
76
+ max_file_size_mb: 20
77
+ max_files_per_request: 10
78
+
61
79
  unknown:
62
80
  supports_native_files: false
63
81
  supports_native_images: false
@@ -3,7 +3,9 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
+ import base64
6
7
  import logging
8
+ import mimetypes
7
9
  from typing import Any, Dict, List, Optional
8
10
 
9
11
  from iatoolkit.common.exceptions import IAToolkitException
@@ -12,12 +14,17 @@ from iatoolkit.infra.llm_response import LLMResponse, ToolCall, Usage
12
14
 
13
15
  class OpenAICompatibleChatAdapter:
14
16
  """
15
- Adapter for OpenAI-compatible Chat Completions APIs.
17
+ Adapter for Chat Completions-style providers.
16
18
 
17
- This adapter is used for providers that expose a `chat.completions` style
18
- interface with `messages`, `tools`, and `tool_calls`.
19
+ This adapter targets providers that expose a `chat.completions` compatible
20
+ endpoint with `messages`, `tools`, `tool_calls`, and `response_format`.
19
21
  """
20
22
 
23
+ supports_multimodal = False
24
+ supports_reasoning = False
25
+ supports_metadata = False
26
+ supports_parallel_tool_calls = False
27
+
21
28
  def __init__(self, openai_compatible_client, provider_label: str = "OpenAI-compatible"):
22
29
  self.client = openai_compatible_client
23
30
  self.provider_label = provider_label
@@ -37,14 +44,10 @@ class OpenAICompatibleChatAdapter:
37
44
  tool_choice = kwargs.get("tool_choice", "auto")
38
45
  context_history = kwargs.get("context_history") or []
39
46
  images = kwargs.get("images") or []
47
+ attachments = kwargs.get("attachments") or []
40
48
  text = kwargs.get("text") or {}
41
-
42
- if images:
43
- logging.warning(
44
- "[%sAdapter] Images provided but these models are not multimodal. Ignoring %s images.",
45
- self.provider_label,
46
- len(images),
47
- )
49
+ reasoning = kwargs.get("reasoning")
50
+ metadata = kwargs.get("metadata")
48
51
 
49
52
  try:
50
53
  messages: List[Dict[str, Any]] = []
@@ -55,6 +58,18 @@ class OpenAICompatibleChatAdapter:
55
58
  current_messages = self._build_messages_from_input(input)
56
59
  messages.extend(current_messages)
57
60
 
61
+ if images or attachments:
62
+ if self.supports_multimodal:
63
+ messages = self._prepare_multimodal_messages(messages, images, attachments)
64
+ else:
65
+ logging.warning(
66
+ "[%sAdapter] Multimodal content provided but this provider is configured as text-only. "
67
+ "Ignoring %s images and %s attachments.",
68
+ self.provider_label,
69
+ len(images),
70
+ len(attachments),
71
+ )
72
+
58
73
  has_function_outputs = any(
59
74
  item.get("type") == "function_call_output" for item in input
60
75
  )
@@ -77,10 +92,25 @@ class OpenAICompatibleChatAdapter:
77
92
  }
78
93
  if tools_payload:
79
94
  call_kwargs["tools"] = tools_payload
80
- if tool_choice:
81
- call_kwargs["tool_choice"] = tool_choice
82
- if isinstance(text, dict) and isinstance(text.get("response_format"), dict):
83
- call_kwargs["response_format"] = text["response_format"]
95
+
96
+ tool_choice_payload = self._map_tool_choice(tool_choice, tools_payload or [])
97
+ if tool_choice_payload is not None:
98
+ call_kwargs["tool_choice"] = tool_choice_payload
99
+
100
+ response_format = self._extract_response_format(text)
101
+ if response_format:
102
+ call_kwargs["response_format"] = response_format
103
+
104
+ if self.supports_reasoning and reasoning:
105
+ call_kwargs["reasoning"] = reasoning
106
+
107
+ if self.supports_metadata and metadata:
108
+ call_kwargs["metadata"] = metadata
109
+
110
+ if self.supports_parallel_tool_calls and kwargs.get("parallel_tool_calls") is not None:
111
+ call_kwargs["parallel_tool_calls"] = bool(kwargs.get("parallel_tool_calls"))
112
+
113
+ self._extend_call_kwargs(call_kwargs, kwargs)
84
114
 
85
115
  logging.debug(
86
116
  "[%sAdapter] Calling chat.completions API with %s messages.",
@@ -100,6 +130,10 @@ class OpenAICompatibleChatAdapter:
100
130
  f"{self.provider_label} error: {ex}"
101
131
  ) from ex
102
132
 
133
+ def _extend_call_kwargs(self, call_kwargs: Dict[str, Any], kwargs: Dict[str, Any]) -> None:
134
+ _ = call_kwargs
135
+ _ = kwargs
136
+
103
137
  def _build_messages_from_input(self, input_items: List[Dict]) -> List[Dict]:
104
138
  messages: List[Dict[str, Any]] = []
105
139
 
@@ -114,29 +148,145 @@ class OpenAICompatibleChatAdapter:
114
148
  )
115
149
  continue
116
150
 
117
- messages.append(
118
- {
119
- "role": "user",
120
- "content": f"Tool result:\n{output}",
121
- }
122
- )
151
+ tool_message: Dict[str, Any] = {
152
+ "role": "tool",
153
+ "content": output,
154
+ }
155
+ call_id = str(item.get("call_id") or "").strip()
156
+ if call_id:
157
+ tool_message["tool_call_id"] = call_id
158
+ messages.append(tool_message)
123
159
  continue
124
160
 
125
- role = item.get("role")
161
+ role = str(item.get("role") or "").strip().lower()
126
162
  content = item.get("content")
127
163
 
128
- if role == "tool":
129
- logging.warning("[%sAdapter] Skipping tool-role message: %s", self.provider_label, item)
130
- continue
131
-
132
164
  if not role:
133
165
  logging.warning("[%sAdapter] Skipping message without role: %s", self.provider_label, item)
134
166
  continue
135
167
 
136
- messages.append({"role": role, "content": content})
168
+ if role == "model":
169
+ role = "assistant"
170
+
171
+ message: Dict[str, Any] = {"role": role, "content": content}
172
+ tool_call_id = str(item.get("tool_call_id") or "").strip()
173
+ if role == "tool" and tool_call_id:
174
+ message["tool_call_id"] = tool_call_id
175
+
176
+ annotations = item.get("annotations")
177
+ if annotations is not None:
178
+ message["annotations"] = annotations
179
+
180
+ messages.append(message)
137
181
 
138
182
  return messages
139
183
 
184
+ def _prepare_multimodal_messages(
185
+ self,
186
+ messages: List[Dict[str, Any]],
187
+ images: List[Dict],
188
+ attachments: List[Dict],
189
+ ) -> List[Dict[str, Any]]:
190
+ target_index = None
191
+ for index in range(len(messages) - 1, -1, -1):
192
+ if messages[index].get("role") == "user":
193
+ target_index = index
194
+ break
195
+
196
+ if target_index is None:
197
+ return messages
198
+
199
+ target_message = dict(messages[target_index])
200
+ content_parts = self._coerce_content_to_parts(target_message.get("content"))
201
+
202
+ for img in images:
203
+ image_part = self._build_image_part(img)
204
+ if image_part is not None:
205
+ content_parts.append(image_part)
206
+
207
+ for attachment in attachments:
208
+ file_part = self._build_file_part(attachment)
209
+ if file_part is not None:
210
+ content_parts.append(file_part)
211
+
212
+ target_message["content"] = content_parts
213
+ updated_messages = list(messages)
214
+ updated_messages[target_index] = target_message
215
+ return updated_messages
216
+
217
+ @staticmethod
218
+ def _coerce_content_to_parts(content: Any) -> List[Dict[str, Any]]:
219
+ if isinstance(content, list):
220
+ return list(content)
221
+ if content in (None, ""):
222
+ return []
223
+ return [{"type": "text", "text": str(content)}]
224
+
225
+ def _build_image_part(self, image: Dict[str, Any]) -> Optional[Dict[str, Any]]:
226
+ if not isinstance(image, dict):
227
+ return None
228
+
229
+ filename = str(image.get("name") or image.get("filename") or "").strip()
230
+ mime_type = str(image.get("mime_type") or image.get("type") or "").strip().lower()
231
+ if not mime_type:
232
+ mime_type = mimetypes.guess_type(filename)[0] or "image/jpeg"
233
+
234
+ raw_url = str(image.get("url") or image.get("image_url") or "").strip()
235
+ if raw_url:
236
+ data_url = raw_url
237
+ else:
238
+ base64_data = str(image.get("base64") or image.get("content") or "").strip()
239
+ if not base64_data:
240
+ return None
241
+ data_url = self._to_data_url(base64_data, mime_type)
242
+
243
+ return {
244
+ "type": "image_url",
245
+ "image_url": {
246
+ "url": data_url,
247
+ },
248
+ }
249
+
250
+ def _build_file_part(self, attachment: Dict[str, Any]) -> Optional[Dict[str, Any]]:
251
+ if not isinstance(attachment, dict):
252
+ return None
253
+
254
+ filename = str(attachment.get("name") or attachment.get("filename") or "attachment").strip()
255
+ mime_type = str(
256
+ attachment.get("mime_type")
257
+ or attachment.get("type")
258
+ or mimetypes.guess_type(filename)[0]
259
+ or "application/octet-stream"
260
+ ).strip().lower()
261
+
262
+ raw_file_data = str(attachment.get("file_data") or attachment.get("url") or "").strip()
263
+ if raw_file_data:
264
+ file_data = raw_file_data
265
+ else:
266
+ base64_data = str(attachment.get("base64") or attachment.get("content") or "").strip()
267
+ if not base64_data:
268
+ return None
269
+ file_data = self._to_data_url(base64_data, mime_type)
270
+
271
+ return {
272
+ "type": "file",
273
+ "file": {
274
+ "filename": filename,
275
+ "file_data": file_data,
276
+ },
277
+ }
278
+
279
+ @staticmethod
280
+ def _to_data_url(base64_data: str, mime_type: str) -> str:
281
+ payload = str(base64_data or "").strip()
282
+ if payload.lower().startswith("data:") and "," in payload:
283
+ return payload
284
+ try:
285
+ base64.b64decode(payload, validate=True)
286
+ return f"data:{mime_type};base64,{payload}"
287
+ except Exception:
288
+ return payload
289
+
140
290
  def _build_tools_payload(self, tools: List[Dict]) -> Optional[List[Dict]]:
141
291
  if not tools:
142
292
  return None
@@ -145,7 +295,7 @@ class OpenAICompatibleChatAdapter:
145
295
 
146
296
  for tool in tools:
147
297
  if "function" in tool:
148
- func_def = tool["function"]
298
+ func_def = dict(tool["function"] or {})
149
299
  else:
150
300
  func_def = {
151
301
  "name": tool.get("name"),
@@ -172,6 +322,41 @@ class OpenAICompatibleChatAdapter:
172
322
 
173
323
  return tools_payload or None
174
324
 
325
+ @staticmethod
326
+ def _map_tool_choice(
327
+ tool_choice: Any,
328
+ tools_payload: List[Dict[str, Any]],
329
+ ) -> Optional[Dict[str, Any] | str]:
330
+ if tool_choice in ("", None, "auto"):
331
+ return None
332
+
333
+ if isinstance(tool_choice, dict):
334
+ return tool_choice
335
+
336
+ if tool_choice in {"none", "required"}:
337
+ return tool_choice
338
+
339
+ tool_names = {
340
+ str((tool.get("function") or {}).get("name") or "").strip()
341
+ for tool in (tools_payload or [])
342
+ if isinstance(tool, dict)
343
+ }
344
+ if str(tool_choice).strip() in tool_names:
345
+ return {
346
+ "type": "function",
347
+ "function": {
348
+ "name": str(tool_choice).strip(),
349
+ },
350
+ }
351
+
352
+ return str(tool_choice)
353
+
354
+ @staticmethod
355
+ def _extract_response_format(text: Dict[str, Any]) -> Optional[Dict[str, Any]]:
356
+ if isinstance(text, dict) and isinstance(text.get("response_format"), dict):
357
+ return dict(text["response_format"])
358
+ return None
359
+
175
360
  def _map_chat_completion_response(self, response: Any) -> LLMResponse:
176
361
  if not response.choices:
177
362
  raise IAToolkitException(
@@ -0,0 +1,62 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ from typing import Any, Dict
7
+
8
+ from iatoolkit.infra.llm_providers.openai_compatible_chat_adapter import OpenAICompatibleChatAdapter
9
+
10
+
11
+ class OpenRouterAdapter(OpenAICompatibleChatAdapter):
12
+ """OpenRouter-specific adapter built on top of the shared chat.completions core."""
13
+
14
+ supports_multimodal = True
15
+ supports_reasoning = False
16
+ supports_metadata = True
17
+ supports_parallel_tool_calls = True
18
+
19
+ def __init__(self, openrouter_client):
20
+ super().__init__(openai_compatible_client=openrouter_client, provider_label="OpenRouter")
21
+
22
+ def _extend_call_kwargs(self, call_kwargs: Dict[str, Any], kwargs: Dict[str, Any]) -> None:
23
+ text = kwargs.get("text") or {}
24
+ verbosity = text.get("verbosity") if isinstance(text, dict) else None
25
+ if verbosity:
26
+ call_kwargs["verbosity"] = verbosity
27
+
28
+ passthrough_keys = (
29
+ "service_tier",
30
+ "temperature",
31
+ "top_p",
32
+ "max_tokens",
33
+ "max_completion_tokens",
34
+ "seed",
35
+ "stop",
36
+ "stream",
37
+ "stream_options",
38
+ "modalities",
39
+ "user",
40
+ )
41
+ for key in passthrough_keys:
42
+ if kwargs.get(key) is not None:
43
+ call_kwargs[key] = kwargs.get(key)
44
+
45
+ extra_body = dict(call_kwargs.get("extra_body") or {})
46
+
47
+ reasoning = kwargs.get("reasoning")
48
+ if isinstance(reasoning, dict) and reasoning:
49
+ extra_body["reasoning"] = dict(reasoning)
50
+
51
+ vendor_specific_keys = (
52
+ "models",
53
+ "provider",
54
+ "plugins",
55
+ "session_id",
56
+ )
57
+ for key in vendor_specific_keys:
58
+ if kwargs.get(key) is not None:
59
+ extra_body[key] = kwargs.get(key)
60
+
61
+ if extra_body:
62
+ call_kwargs["extra_body"] = extra_body
@@ -7,7 +7,9 @@
7
7
  from iatoolkit.services.configuration_service import ConfigurationService
8
8
  from iatoolkit.infra.llm_providers.openai_adapter import OpenAIAdapter
9
9
  from iatoolkit.infra.llm_providers.gemini_adapter import GeminiAdapter
10
+ from iatoolkit.infra.llm_providers.deepseek_adapter import DeepseekAdapter
10
11
  from iatoolkit.infra.llm_providers.openai_compatible_chat_adapter import OpenAICompatibleChatAdapter
12
+ from iatoolkit.infra.llm_providers.openrouter_adapter import OpenRouterAdapter
11
13
  from iatoolkit.infra.llm_providers.anthropic_adapter import AnthropicAdapter
12
14
  from iatoolkit.common.exceptions import IAToolkitException
13
15
  from iatoolkit.common.util import Utility
@@ -39,6 +41,7 @@ class LLMProxy:
39
41
  PROVIDER_XAI = "xai"
40
42
  PROVIDER_ANTHROPIC = "anthropic"
41
43
  PROVIDER_OPENAI_COMPATIBLE = "openai_compatible"
44
+ PROVIDER_OPENROUTER = "openrouter"
42
45
  DEFAULT_CONNECT_TIMEOUT_SECONDS = 10.0
43
46
  DEFAULT_READ_TIMEOUT_SECONDS = 300.0
44
47
  DEFAULT_MAX_RETRIES = 0
@@ -145,6 +148,7 @@ class LLMProxy:
145
148
  client_config = self._get_client_config(company_short_name, provider)
146
149
  api_key = client_config["api_key"]
147
150
  base_url = client_config.get("base_url") or ""
151
+ default_headers = client_config.get("default_headers") or {}
148
152
  connect_timeout_seconds = client_config["connect_timeout_seconds"]
149
153
  read_timeout_seconds = client_config["read_timeout_seconds"]
150
154
  max_retries = client_config["max_retries"]
@@ -152,6 +156,7 @@ class LLMProxy:
152
156
  provider,
153
157
  api_key or "",
154
158
  base_url,
159
+ tuple(sorted(default_headers.items())),
155
160
  connect_timeout_seconds,
156
161
  read_timeout_seconds,
157
162
  max_retries,
@@ -166,6 +171,7 @@ class LLMProxy:
166
171
  provider,
167
172
  api_key,
168
173
  base_url=base_url,
174
+ default_headers=default_headers,
169
175
  connect_timeout_seconds=connect_timeout_seconds,
170
176
  read_timeout_seconds=read_timeout_seconds,
171
177
  max_retries=max_retries,
@@ -177,9 +183,11 @@ class LLMProxy:
177
183
  elif provider == self.PROVIDER_GEMINI:
178
184
  adapter = GeminiAdapter(client)
179
185
  elif provider == self.PROVIDER_DEEPSEEK:
180
- adapter = OpenAICompatibleChatAdapter(client, provider_label="DeepSeek")
186
+ adapter = DeepseekAdapter(client)
181
187
  elif provider == self.PROVIDER_OPENAI_COMPATIBLE:
182
188
  adapter = OpenAICompatibleChatAdapter(client, provider_label="OpenAI-compatible")
189
+ elif provider == self.PROVIDER_OPENROUTER:
190
+ adapter = OpenRouterAdapter(client)
183
191
  elif provider == self.PROVIDER_ANTHROPIC:
184
192
  adapter = AnthropicAdapter(client)
185
193
  else:
@@ -199,7 +207,7 @@ class LLMProxy:
199
207
  ) -> Dict[str, Any]:
200
208
  effective_kwargs = dict(request_kwargs or {})
201
209
 
202
- if provider != self.PROVIDER_OPENAI_COMPATIBLE:
210
+ if provider not in {self.PROVIDER_OPENAI_COMPATIBLE, self.PROVIDER_OPENROUTER}:
203
211
  return effective_kwargs
204
212
 
205
213
  if provider_config.get("disable_tools") is True:
@@ -219,6 +227,7 @@ class LLMProxy:
219
227
  provider: str,
220
228
  api_key: str,
221
229
  base_url: str = "",
230
+ default_headers: Dict[str, str] | None = None,
222
231
  *,
223
232
  connect_timeout_seconds: float | None = None,
224
233
  read_timeout_seconds: float | None = None,
@@ -244,6 +253,7 @@ class LLMProxy:
244
253
  provider,
245
254
  api_key or "",
246
255
  base_url or "",
256
+ tuple(sorted((default_headers or {}).items())),
247
257
  connect_timeout_seconds,
248
258
  read_timeout_seconds,
249
259
  max_retries,
@@ -257,6 +267,7 @@ class LLMProxy:
257
267
  provider,
258
268
  api_key,
259
269
  base_url=base_url,
270
+ default_headers=default_headers,
260
271
  connect_timeout_seconds=connect_timeout_seconds,
261
272
  read_timeout_seconds=read_timeout_seconds,
262
273
  max_retries=max_retries,
@@ -269,6 +280,7 @@ class LLMProxy:
269
280
  provider: str,
270
281
  api_key: str,
271
282
  base_url: str = "",
283
+ default_headers: Dict[str, str] | None = None,
272
284
  *,
273
285
  connect_timeout_seconds: float | None = None,
274
286
  read_timeout_seconds: float | None = None,
@@ -325,6 +337,20 @@ class LLMProxy:
325
337
  max_retries=max_retries,
326
338
  )
327
339
 
340
+ if provider == self.PROVIDER_OPENROUTER:
341
+ if not base_url:
342
+ raise IAToolkitException(
343
+ IAToolkitException.ErrorType.CONFIG_ERROR,
344
+ "Provider 'openrouter' requires a configured base_url."
345
+ )
346
+ return OpenAI(
347
+ api_key=api_key,
348
+ base_url=base_url,
349
+ timeout=timeout,
350
+ max_retries=max_retries,
351
+ default_headers=default_headers or None,
352
+ )
353
+
328
354
  if provider == self.PROVIDER_GEMINI:
329
355
  # Example placeholder: you may already have a Gemini client factory elsewhere.
330
356
  # Here you could create and configure the Gemini client (e.g. google.generativeai).
@@ -353,6 +379,7 @@ class LLMProxy:
353
379
  return {
354
380
  "api_key": self._get_api_key_from_config(company_short_name, provider),
355
381
  "base_url": self._get_base_url_from_config(company_short_name, provider),
382
+ "default_headers": self._get_default_headers_from_config(company_short_name, provider),
356
383
  "connect_timeout_seconds": self._resolve_provider_float(
357
384
  provider_config,
358
385
  ("connect_timeout_seconds",),
@@ -499,7 +526,7 @@ class LLMProxy:
499
526
  return api_key_value
500
527
 
501
528
  def _get_base_url_from_config(self, company_short_name: str, provider: str) -> str:
502
- if provider != self.PROVIDER_OPENAI_COMPATIBLE:
529
+ if provider not in {self.PROVIDER_OPENAI_COMPATIBLE, self.PROVIDER_OPENROUTER}:
503
530
  return ""
504
531
 
505
532
  provider_config = self.configuration_service.get_llm_provider_config(company_short_name, provider) or {}
@@ -528,6 +555,27 @@ class LLMProxy:
528
555
 
529
556
  return base_url
530
557
 
558
+ def _get_default_headers_from_config(self, company_short_name: str, provider: str) -> dict:
559
+ if provider != self.PROVIDER_OPENROUTER:
560
+ return {}
561
+
562
+ provider_config = self.configuration_service.get_llm_provider_config(company_short_name, provider) or {}
563
+ if not isinstance(provider_config, dict):
564
+ provider_config = {}
565
+
566
+ headers: dict[str, str] = {}
567
+
568
+ http_referer = str(provider_config.get("http_referer") or "").strip()
569
+ if http_referer:
570
+ headers["HTTP-Referer"] = http_referer
571
+
572
+ x_title = str(provider_config.get("x_title") or "").strip()
573
+ if x_title:
574
+ headers["X-Title"] = x_title
575
+ headers["X-OpenRouter-Title"] = x_title
576
+
577
+ return headers
578
+
531
579
  @classmethod
532
580
  def clear_low_level_clients_cache(cls):
533
581
  with cls._clients_cache_lock:
@@ -342,6 +342,7 @@ class ConfigurationService:
342
342
  "xai",
343
343
  "anthropic",
344
344
  "openai_compatible",
345
+ "openrouter",
345
346
  }
346
347
  llm_defaults_mode = str(config.get("llm", {}).get("default_attachment_mode", "extracted_only")).strip().lower()
347
348
  llm_defaults_fallback = str(config.get("llm", {}).get("default_attachment_fallback", "extract")).strip().lower()
@@ -360,6 +361,7 @@ class ConfigurationService:
360
361
 
361
362
  llm_available_models = config.get("llm", {}).get("available_models") or []
362
363
  openai_compatible_in_use = False
364
+ openrouter_in_use = False
363
365
  for index, model_entry in enumerate(llm_available_models):
364
366
  normalized_model = self._normalize_model_entry(model_entry)
365
367
  provider = str(normalized_model.get("provider") or "").strip().lower()
@@ -370,6 +372,8 @@ class ConfigurationService:
370
372
  )
371
373
  if provider == "openai_compatible":
372
374
  openai_compatible_in_use = True
375
+ if provider == "openrouter":
376
+ openrouter_in_use = True
373
377
 
374
378
  providers_cfg = config.get("llm", {}).get("providers")
375
379
  if providers_cfg is not None and not isinstance(providers_cfg, dict):
@@ -403,6 +407,49 @@ class ConfigurationService:
403
407
  "Must be an absolute HTTP or HTTPS URL.",
404
408
  )
405
409
 
410
+ if openrouter_in_use:
411
+ provider_cfg = config.get("llm", {}).get("openrouter")
412
+ if not isinstance(provider_cfg, dict):
413
+ add_error(
414
+ "llm.openrouter",
415
+ "Provider configuration is required and must be a dictionary.",
416
+ )
417
+ else:
418
+ has_base_url = any(
419
+ str(provider_cfg.get(key) or "").strip()
420
+ for key in ("base_url", "base_url_env", "base_url_secret_ref")
421
+ )
422
+ if not has_base_url:
423
+ add_error(
424
+ "llm.openrouter",
425
+ "One of 'base_url', 'base_url_env', or 'base_url_secret_ref' is required.",
426
+ )
427
+
428
+ raw_base_url = str(provider_cfg.get("base_url") or "").strip()
429
+ if raw_base_url:
430
+ parsed = urlparse(raw_base_url)
431
+ if parsed.scheme.lower() not in {"http", "https"} or not parsed.netloc:
432
+ add_error(
433
+ "llm.openrouter.base_url",
434
+ "Must be an absolute HTTP or HTTPS URL.",
435
+ )
436
+
437
+ raw_http_referer = provider_cfg.get("http_referer")
438
+ if raw_http_referer is not None:
439
+ if not isinstance(raw_http_referer, str) or not raw_http_referer.strip():
440
+ add_error("llm.openrouter.http_referer", "Must be a non-empty string.")
441
+ else:
442
+ parsed = urlparse(raw_http_referer.strip())
443
+ if parsed.scheme.lower() not in {"http", "https"} or not parsed.netloc:
444
+ add_error(
445
+ "llm.openrouter.http_referer",
446
+ "Must be an absolute HTTP or HTTPS URL.",
447
+ )
448
+
449
+ raw_x_title = provider_cfg.get("x_title")
450
+ if raw_x_title is not None and (not isinstance(raw_x_title, str) or not raw_x_title.strip()):
451
+ add_error("llm.openrouter.x_title", "Must be a non-empty string.")
452
+
406
453
  # 3. Embedding Provider
407
454
  if isinstance(config.get("embedding_provider"), dict):
408
455
  if not config.get("embedding_provider", {}).get("provider"):
@@ -938,6 +985,10 @@ class ConfigurationService:
938
985
 
939
986
  self._ensure_config_loaded(company_short_name)
940
987
  llm_config = self._loaded_configs[company_short_name].get("llm") or {}
988
+ direct_provider_cfg = llm_config.get(candidate)
989
+ if isinstance(direct_provider_cfg, dict):
990
+ return dict(direct_provider_cfg)
991
+
941
992
  providers_cfg = llm_config.get("providers")
942
993
  if not isinstance(providers_cfg, dict):
943
994
  return {}