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.
- {iatoolkit-2.3.4/src/iatoolkit.egg-info → iatoolkit-2.4.1}/PKG-INFO +1 -1
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/__init__.py +1 -1
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/config/llm_capabilities.yaml +18 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/config/system_prompts_pack.yaml +0 -1
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +212 -27
- iatoolkit-2.4.1/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +62 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_proxy.py +86 -3
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/locales/en.yaml +3 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/locales/es.yaml +3 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/configuration_service.py +165 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/knowledge_base_service.py +17 -1
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/query_service.py +16 -2
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/html_structures.prompt +1 -1
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/rag_api_view.py +73 -1
- {iatoolkit-2.3.4 → iatoolkit-2.4.1/src/iatoolkit.egg-info}/PKG-INFO +1 -1
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/LICENSE +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/pyproject.toml +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/readme.md +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/requirements.txt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/setup.cfg +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/cli_commands.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/memory_lint_trigger.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/model_registry.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/routes.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/secret_resolver.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/common/util.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/config/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/config/system_tools_pack.yaml +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/core.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/google_auth_client.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/inference_handler.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/llm_response.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/api_key_repo.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/database_manager.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/document_repo.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/memory_repo.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/models.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/profile_repo.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/repositories/vs_repo.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/api_key_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/attachment_policy_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/auth_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/benchmark_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/branding_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/company_context_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/context_builder_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/dispatcher_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/embedding_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/file_processor_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/history_manager_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/http_tool_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/i18n_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/inference_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/language_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/license_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/llm_client_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_compiler_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_lint_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/memory_wiki_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/noop_memory_lint_trigger.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/contracts.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/parsers/validator.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/pdf_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/profile_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/prompt_resource_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/prompt_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/sql_dataset_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/sql_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/sql_source_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/storage_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/structured_output_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/system_tools.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/tool_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/user_feedback_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/visual_kb_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/visual_tool_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/warmup_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/services/web_search_service.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_filepond.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_help_content.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_history_button.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_main.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/chat_modal.css +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/chat_public.css +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/documents.css +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/landing_page.css +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/chat_state_rules.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/chat_user_profile.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/core_identity.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/email_output.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/file_download_output.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/links_documents.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/memory_usage.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/multimodal_basics.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/output_basics.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/presentation_formatting.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/query_main.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_aggregation_scope.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_casting.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_core.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_jsonb.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/system_prompts/tool_html_passthrough.prompt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/_company_header.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/_login_widget.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/change_password.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/chat.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/chat_modals.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/error.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/home_hosted_default.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/onboarding_shell.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/pdf/base.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/pdf/letter.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/pdf/report.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/pdf/simple.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/api_key_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/base_login_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/categories_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/change_password_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/chat_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/configuration_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/connectors_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/embedding_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/forgot_password_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/help_content_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/history_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/home_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/load_document_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/login_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/memory_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/profile_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/prompt_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/root_redirect_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/signup_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/static_page_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/tool_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/users_api_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit/views/verify_user_view.py +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-2.3.4 → iatoolkit-2.4.1}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -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
|
|
17
|
+
Adapter for Chat Completions-style providers.
|
|
16
18
|
|
|
17
|
-
This adapter
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
if
|
|
83
|
-
call_kwargs["
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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"
|