iatoolkit 2.3.4__tar.gz → 2.4.1__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.1}/PKG-INFO +1 -1
  2. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/__init__.py +1 -1
  3. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/config/llm_capabilities.yaml +18 -0
  4. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/config/system_prompts_pack.yaml +0 -1
  5. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +212 -27
  6. iatoolkit-2.4.1/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +62 -0
  7. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_proxy.py +86 -3
  8. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/locales/en.yaml +3 -0
  9. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/locales/es.yaml +3 -0
  10. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/configuration_service.py +165 -0
  11. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/knowledge_base_service.py +17 -1
  12. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/query_service.py +16 -2
  13. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/html_structures.prompt +1 -1
  14. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/rag_api_view.py +73 -1
  15. {iatoolkit-2.3.4 → iatoolkit-2.4.1/src/iatoolkit.egg-info}/PKG-INFO +1 -1
  16. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
  17. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/LICENSE +0 -0
  18. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/LICENSE_COMMUNITY.md +0 -0
  19. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/pyproject.toml +0 -0
  20. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/readme.md +0 -0
  21. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/requirements.txt +0 -0
  22. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/setup.cfg +0 -0
  23. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/base_company.py +0 -0
  24. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/cli_commands.py +0 -0
  25. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/__init__.py +0 -0
  26. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/exceptions.py +0 -0
  27. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/__init__.py +0 -0
  28. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
  29. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
  30. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
  31. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/memory_lint_trigger.py +0 -0
  32. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
  33. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
  34. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
  35. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/model_registry.py +0 -0
  36. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/routes.py +0 -0
  37. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/secret_resolver.py +0 -0
  38. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/session_manager.py +0 -0
  39. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/util.py +0 -0
  40. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/company_registry.py +0 -0
  41. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/config/__init__.py +0 -0
  42. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
  43. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/core.py +0 -0
  44. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/__init__.py +0 -0
  45. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
  46. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/call_service.py +0 -0
  47. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  48. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  49. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  50. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  51. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  52. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  53. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  54. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/google_auth_client.py +0 -0
  55. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/google_chat_app.py +0 -0
  56. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
  57. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/inference_handler.py +0 -0
  58. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
  59. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
  60. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
  61. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
  62. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
  63. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
  64. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_response.py +0 -0
  65. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  66. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/__init__.py +0 -0
  67. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/api_key_repo.py +0 -0
  68. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/database_manager.py +0 -0
  69. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/document_repo.py +0 -0
  70. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
  71. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
  72. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  73. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/memory_repo.py +0 -0
  74. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/models.py +0 -0
  75. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/profile_repo.py +0 -0
  76. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
  77. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
  78. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
  79. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/vs_repo.py +0 -0
  80. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/__init__.py +0 -0
  81. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/api_key_service.py +0 -0
  82. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/attachment_policy_service.py +0 -0
  83. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/auth_service.py +0 -0
  84. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/benchmark_service.py +0 -0
  85. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/branding_service.py +0 -0
  86. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/company_context_service.py +0 -0
  87. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/context_builder_service.py +0 -0
  88. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/dispatcher_service.py +0 -0
  89. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/embedding_service.py +0 -0
  90. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/excel_service.py +0 -0
  91. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/file_processor_service.py +0 -0
  92. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/history_manager_service.py +0 -0
  93. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/http_tool_service.py +0 -0
  94. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/i18n_service.py +0 -0
  95. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/inference_service.py +0 -0
  96. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/jwt_service.py +0 -0
  97. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/language_service.py +0 -0
  98. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/license_service.py +0 -0
  99. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/llm_client_service.py +0 -0
  100. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/mail_service.py +0 -0
  101. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_compiler_service.py +0 -0
  102. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_lint_service.py +0 -0
  103. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
  104. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_service.py +0 -0
  105. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_wiki_service.py +0 -0
  106. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
  107. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/noop_memory_lint_trigger.py +0 -0
  108. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/__init__.py +0 -0
  109. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/contracts.py +0 -0
  110. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
  111. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
  112. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
  113. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
  114. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
  115. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
  116. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
  117. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
  118. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/validator.py +0 -0
  119. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/pdf_service.py +0 -0
  120. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/profile_service.py +0 -0
  121. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/prompt_resource_service.py +0 -0
  122. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/prompt_service.py +0 -0
  123. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
  124. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/sql_dataset_service.py +0 -0
  125. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/sql_service.py +0 -0
  126. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/sql_source_service.py +0 -0
  127. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/storage_service.py +0 -0
  128. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/structured_output_service.py +0 -0
  129. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
  130. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/system_tools.py +0 -0
  131. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/tool_service.py +0 -0
  132. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/user_feedback_service.py +0 -0
  133. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/user_session_context_service.py +0 -0
  134. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/visual_kb_service.py +0 -0
  135. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/visual_tool_service.py +0 -0
  136. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/warmup_service.py +0 -0
  137. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search/__init__.py +0 -0
  138. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
  139. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
  140. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
  141. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search_service.py +0 -0
  142. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  143. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
  144. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
  145. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
  146. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  147. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_help_content.js +0 -0
  148. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_history_button.js +0 -0
  149. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
  150. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_main.js +0 -0
  151. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
  152. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
  153. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
  154. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
  155. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
  156. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
  157. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  158. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/chat_public.css +0 -0
  159. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/documents.css +0 -0
  160. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/landing_page.css +0 -0
  161. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/llm_output.css +0 -0
  162. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/onboarding.css +0 -0
  163. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/__init__.py +0 -0
  164. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/chat_state_rules.prompt +0 -0
  165. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/chat_user_profile.prompt +0 -0
  166. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/core_identity.prompt +0 -0
  167. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/email_output.prompt +0 -0
  168. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/file_download_output.prompt +0 -0
  169. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
  170. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/links_documents.prompt +0 -0
  171. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/memory_usage.prompt +0 -0
  172. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/multimodal_basics.prompt +0 -0
  173. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/output_basics.prompt +0 -0
  174. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/presentation_formatting.prompt +0 -0
  175. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/query_main.prompt +0 -0
  176. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_aggregation_scope.prompt +0 -0
  177. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_casting.prompt +0 -0
  178. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_core.prompt +0 -0
  179. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_jsonb.prompt +0 -0
  180. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
  181. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/tool_html_passthrough.prompt +0 -0
  182. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/_company_header.html +0 -0
  183. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/_login_widget.html +0 -0
  184. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/base.html +0 -0
  185. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/change_password.html +0 -0
  186. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/chat.html +0 -0
  187. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
  188. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/chat_modals.html +0 -0
  189. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/error.html +0 -0
  190. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/forgot_password.html +0 -0
  191. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/home_hosted_default.html +0 -0
  192. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
  193. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  194. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/pdf/base.html +0 -0
  195. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/pdf/letter.html +0 -0
  196. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/pdf/report.html +0 -0
  197. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/pdf/simple.html +0 -0
  198. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/signup.html +0 -0
  199. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/__init__.py +0 -0
  200. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/api_key_api_view.py +0 -0
  201. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/base_login_view.py +0 -0
  202. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/categories_api_view.py +0 -0
  203. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/change_password_view.py +0 -0
  204. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
  205. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/chat_view.py +0 -0
  206. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/configuration_api_view.py +0 -0
  207. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/connectors_api_view.py +0 -0
  208. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/embedding_api_view.py +0 -0
  209. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/forgot_password_view.py +0 -0
  210. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/help_content_api_view.py +0 -0
  211. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/history_api_view.py +0 -0
  212. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/home_view.py +0 -0
  213. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/init_context_api_view.py +0 -0
  214. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/llmquery_api_view.py +0 -0
  215. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/load_document_api_view.py +0 -0
  216. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/login_view.py +0 -0
  217. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/logout_api_view.py +0 -0
  218. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/memory_api_view.py +0 -0
  219. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/profile_api_view.py +0 -0
  220. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/prompt_api_view.py +0 -0
  221. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
  222. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
  223. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/root_redirect_view.py +0 -0
  224. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/signup_view.py +0 -0
  225. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/static_page_view.py +0 -0
  226. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/tool_api_view.py +0 -0
  227. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
  228. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/users_api_view.py +0 -0
  229. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/verify_user_view.py +0 -0
  230. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  231. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit.egg-info/requires.txt +0 -0
  232. {iatoolkit-2.3.4 → iatoolkit-2.4.1}/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.1
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.1"
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
@@ -71,7 +71,6 @@ prompts:
71
71
  include:
72
72
  execution_modes:
73
73
  - chat
74
- - agent
75
74
  - key: file_download_output
76
75
  filename: file_download_output.prompt
77
76
  section: output_contract
@@ -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
@@ -83,6 +86,7 @@ class LLMProxy:
83
86
  company_short_name=company_short_name,
84
87
  model=model,
85
88
  )
89
+ model_config = self.configuration_service.get_llm_model_config(company_short_name, model) or {}
86
90
  provider_config = self.configuration_service.get_llm_provider_config(company_short_name, provider) or {}
87
91
 
88
92
  adapter = self._get_or_create_adapter(
@@ -95,6 +99,11 @@ class LLMProxy:
95
99
  provider_config=provider_config,
96
100
  request_kwargs=kwargs,
97
101
  )
102
+ request_kwargs = self._apply_model_request_overrides(
103
+ provider=provider,
104
+ model_config=model_config,
105
+ request_kwargs=request_kwargs,
106
+ )
98
107
 
99
108
  # Delegate to the adapter (OpenAI, Gemini, DeepSeek, xAI, Anthropic, etc.)
100
109
  return adapter.create_response(model=model, input=input, **request_kwargs)
@@ -145,6 +154,7 @@ class LLMProxy:
145
154
  client_config = self._get_client_config(company_short_name, provider)
146
155
  api_key = client_config["api_key"]
147
156
  base_url = client_config.get("base_url") or ""
157
+ default_headers = client_config.get("default_headers") or {}
148
158
  connect_timeout_seconds = client_config["connect_timeout_seconds"]
149
159
  read_timeout_seconds = client_config["read_timeout_seconds"]
150
160
  max_retries = client_config["max_retries"]
@@ -152,6 +162,7 @@ class LLMProxy:
152
162
  provider,
153
163
  api_key or "",
154
164
  base_url,
165
+ tuple(sorted(default_headers.items())),
155
166
  connect_timeout_seconds,
156
167
  read_timeout_seconds,
157
168
  max_retries,
@@ -166,6 +177,7 @@ class LLMProxy:
166
177
  provider,
167
178
  api_key,
168
179
  base_url=base_url,
180
+ default_headers=default_headers,
169
181
  connect_timeout_seconds=connect_timeout_seconds,
170
182
  read_timeout_seconds=read_timeout_seconds,
171
183
  max_retries=max_retries,
@@ -177,9 +189,11 @@ class LLMProxy:
177
189
  elif provider == self.PROVIDER_GEMINI:
178
190
  adapter = GeminiAdapter(client)
179
191
  elif provider == self.PROVIDER_DEEPSEEK:
180
- adapter = OpenAICompatibleChatAdapter(client, provider_label="DeepSeek")
192
+ adapter = DeepseekAdapter(client)
181
193
  elif provider == self.PROVIDER_OPENAI_COMPATIBLE:
182
194
  adapter = OpenAICompatibleChatAdapter(client, provider_label="OpenAI-compatible")
195
+ elif provider == self.PROVIDER_OPENROUTER:
196
+ adapter = OpenRouterAdapter(client)
183
197
  elif provider == self.PROVIDER_ANTHROPIC:
184
198
  adapter = AnthropicAdapter(client)
185
199
  else:
@@ -199,7 +213,7 @@ class LLMProxy:
199
213
  ) -> Dict[str, Any]:
200
214
  effective_kwargs = dict(request_kwargs or {})
201
215
 
202
- if provider != self.PROVIDER_OPENAI_COMPATIBLE:
216
+ if provider not in {self.PROVIDER_OPENAI_COMPATIBLE, self.PROVIDER_OPENROUTER}:
203
217
  return effective_kwargs
204
218
 
205
219
  if provider_config.get("disable_tools") is True:
@@ -210,6 +224,35 @@ class LLMProxy:
210
224
 
211
225
  return effective_kwargs
212
226
 
227
+ def _apply_model_request_overrides(
228
+ self,
229
+ provider: str,
230
+ model_config: Dict[str, Any],
231
+ request_kwargs: Dict[str, Any],
232
+ ) -> Dict[str, Any]:
233
+ effective_kwargs = dict(request_kwargs or {})
234
+
235
+ if provider != self.PROVIDER_OPENROUTER or not isinstance(model_config, dict):
236
+ return effective_kwargs
237
+
238
+ model_runtime_config = model_config.get("config")
239
+ if not isinstance(model_runtime_config, dict):
240
+ return effective_kwargs
241
+
242
+ openrouter_provider = model_runtime_config.get("routing")
243
+ if not isinstance(openrouter_provider, dict) or not openrouter_provider:
244
+ return effective_kwargs
245
+
246
+ existing_provider = effective_kwargs.get("provider")
247
+ if isinstance(existing_provider, dict):
248
+ merged_provider = dict(openrouter_provider)
249
+ merged_provider.update(existing_provider)
250
+ effective_kwargs["provider"] = merged_provider
251
+ elif existing_provider is None:
252
+ effective_kwargs["provider"] = dict(openrouter_provider)
253
+
254
+ return effective_kwargs
255
+
213
256
  # -------------------------------------------------------------------------
214
257
  # Client cache
215
258
  # -------------------------------------------------------------------------
@@ -219,6 +262,7 @@ class LLMProxy:
219
262
  provider: str,
220
263
  api_key: str,
221
264
  base_url: str = "",
265
+ default_headers: Dict[str, str] | None = None,
222
266
  *,
223
267
  connect_timeout_seconds: float | None = None,
224
268
  read_timeout_seconds: float | None = None,
@@ -244,6 +288,7 @@ class LLMProxy:
244
288
  provider,
245
289
  api_key or "",
246
290
  base_url or "",
291
+ tuple(sorted((default_headers or {}).items())),
247
292
  connect_timeout_seconds,
248
293
  read_timeout_seconds,
249
294
  max_retries,
@@ -257,6 +302,7 @@ class LLMProxy:
257
302
  provider,
258
303
  api_key,
259
304
  base_url=base_url,
305
+ default_headers=default_headers,
260
306
  connect_timeout_seconds=connect_timeout_seconds,
261
307
  read_timeout_seconds=read_timeout_seconds,
262
308
  max_retries=max_retries,
@@ -269,6 +315,7 @@ class LLMProxy:
269
315
  provider: str,
270
316
  api_key: str,
271
317
  base_url: str = "",
318
+ default_headers: Dict[str, str] | None = None,
272
319
  *,
273
320
  connect_timeout_seconds: float | None = None,
274
321
  read_timeout_seconds: float | None = None,
@@ -325,6 +372,20 @@ class LLMProxy:
325
372
  max_retries=max_retries,
326
373
  )
327
374
 
375
+ if provider == self.PROVIDER_OPENROUTER:
376
+ if not base_url:
377
+ raise IAToolkitException(
378
+ IAToolkitException.ErrorType.CONFIG_ERROR,
379
+ "Provider 'openrouter' requires a configured base_url."
380
+ )
381
+ return OpenAI(
382
+ api_key=api_key,
383
+ base_url=base_url,
384
+ timeout=timeout,
385
+ max_retries=max_retries,
386
+ default_headers=default_headers or None,
387
+ )
388
+
328
389
  if provider == self.PROVIDER_GEMINI:
329
390
  # Example placeholder: you may already have a Gemini client factory elsewhere.
330
391
  # Here you could create and configure the Gemini client (e.g. google.generativeai).
@@ -353,6 +414,7 @@ class LLMProxy:
353
414
  return {
354
415
  "api_key": self._get_api_key_from_config(company_short_name, provider),
355
416
  "base_url": self._get_base_url_from_config(company_short_name, provider),
417
+ "default_headers": self._get_default_headers_from_config(company_short_name, provider),
356
418
  "connect_timeout_seconds": self._resolve_provider_float(
357
419
  provider_config,
358
420
  ("connect_timeout_seconds",),
@@ -499,7 +561,7 @@ class LLMProxy:
499
561
  return api_key_value
500
562
 
501
563
  def _get_base_url_from_config(self, company_short_name: str, provider: str) -> str:
502
- if provider != self.PROVIDER_OPENAI_COMPATIBLE:
564
+ if provider not in {self.PROVIDER_OPENAI_COMPATIBLE, self.PROVIDER_OPENROUTER}:
503
565
  return ""
504
566
 
505
567
  provider_config = self.configuration_service.get_llm_provider_config(company_short_name, provider) or {}
@@ -528,6 +590,27 @@ class LLMProxy:
528
590
 
529
591
  return base_url
530
592
 
593
+ def _get_default_headers_from_config(self, company_short_name: str, provider: str) -> dict:
594
+ if provider != self.PROVIDER_OPENROUTER:
595
+ return {}
596
+
597
+ provider_config = self.configuration_service.get_llm_provider_config(company_short_name, provider) or {}
598
+ if not isinstance(provider_config, dict):
599
+ provider_config = {}
600
+
601
+ headers: dict[str, str] = {}
602
+
603
+ http_referer = str(provider_config.get("http_referer") or "").strip()
604
+ if http_referer:
605
+ headers["HTTP-Referer"] = http_referer
606
+
607
+ x_title = str(provider_config.get("x_title") or "").strip()
608
+ if x_title:
609
+ headers["X-Title"] = x_title
610
+ headers["X-OpenRouter-Title"] = x_title
611
+
612
+ return headers
613
+
531
614
  @classmethod
532
615
  def clear_low_level_clients_cache(cls):
533
616
  with cls._clients_cache_lock:
@@ -1286,6 +1286,9 @@ ui:
1286
1286
  status_failed: "Failed"
1287
1287
  created_at: "Created"
1288
1288
  date_range: "Date range"
1289
+ metadata_field: "Metadata field"
1290
+ metadata_value: "Metadata value"
1291
+ select_metadata_field: "Select a field"
1289
1292
  delete_confirmation: "Delete File?"
1290
1293
  delete_message: "This action cannot be undone. The file will be permanently removed."
1291
1294
  delete_button: "Delete"
@@ -1290,6 +1290,9 @@ ui:
1290
1290
  status_failed: "Fallido"
1291
1291
  created_at: "Creado"
1292
1292
  date_range: "Rango de fechas"
1293
+ metadata_field: "Campo metadata"
1294
+ metadata_value: "Valor metadata"
1295
+ select_metadata_field: "Selecciona un campo"
1293
1296
  delete_confirmation: "¿Eliminar archivo?"
1294
1297
  delete_message: "Esta acción no se puede deshacer. El archivo se eliminará permanentemente."
1295
1298
  delete_button: "Eliminar"