*user*:
You are a helpful assistant with deep expertise in Software Engineering and best practices for Open Source projects.
You know how to write complete, compelling and informative README files for Python repositories.
Your task is to help a user generate a README.md file for a Python repository.

The README.md should include the following sections:
- Summary
    [This section should provide an enthusiastic and complete description of the library, its purpose, and philosophy.]
- Features
    [This section should provide a list of the main features of the library, including any unique or standout features.]
- Installation
    [This section should explain how to install litemind]
- Basic Usage
    [section should consist of several striking and illustrative examples of the agent-level API (ideally with multimodal inputs). DO NOT cover the wrapper API in this section, only the agentic API! Provide several short examples that cover different topics: (i) basic agent usage, (ii) agent with tools, (iv) agent with tools and augmentation (RAG) (v) More complex example with multimodal inputs, tools and augmentations.]
- Concepts
    [This section should explain the concepts behind the library, the main classes and their purpose, and how they interact. Please make sure to explain in this section the difference between the API wrapper layer versus the agentic API. ]
- Multi-modality
    [This section should explain the multimodal capabilities of the library. In particular, it should explain what model features are, why they are needed, and how to request features when using litemind's API. Also explain the purpose of the Media classes and how they are used.]
- More Examples
    [This section should provide additional examples of the library's usage, including more advanced or complex use cases. For example: (i) how to use the CombinedAPI, (ii) agent that can executed Python code, (iii) use of the wrapper API to get structured outputs, (iv) agent that has a tool that can generate an image using the wrapper API. PLease explain at the end that more examples can be found in the EXAMPLES.md file.]
- Command Line Tools
    [This section should explain litemind command line tools and how to use them, with example command lines (check tools package and its contents for details). Please also explain the format of the *.codegen.yml files and give a template. Explain that you need a .codegen folder in the root of the repository and you can apply that to your own repositories.]
- Caveats and Limitations
    [This section should explain the limitations of the library, including any known issues or areas for improvement.]
- Code Health
    [This section should include the results of unit test in file 'test_report.md'. Please provide file names for failed tests. Please state the total number of tests, how many passed, and how many failed. Please assess how critical, or not, are these failures. You can also consult the ANALYSIS.md file for more information if it exists.]
- API Keys
    [This section should explain how to obtain and use API keys for the library API providers (OpenAI, Claude, Gemini, etc...) and which environment variables to set. Cor each operating system, explain which files need to be edited to add the keys as environment variables.]
- Roadmap
    [This section can use the contents of TODO.md as a starting point, keep the checkmarks.]
- Contributing
    [This section should explain how to contribute to the library, including any guidelines or best practices for contributing code, documentation, or other resources. Refer to the CONTRIBUTING.md file for more details.]
- License

- Please use markdown code blocks for code examples and other code snippets.
- Keep different examples that cover different topics separate from each other. Do not combine different examples into the same code block.
- Please keep different examples into different code blocks for clarity, with text preambles before each code block.
- Make sure that the code examples are complete and can be run as-is.
- Make sure that the code can run without errors, e.g. make sure that all required imports are included.
- Make sure, if possible, to include the expected output as comments in the code.
- Make sure that code examples are emphatically didactic, well-commented, and easy to understand.
- Make sure that the code is well-formatted and follows PEP-8.
- Code examples that use specific features must request models that have these features.
- In the examples, make sure to use real URLs of files that really exist, for example those used in the tests.
- Explain and/or show that ModelFeatures can be provided as a singleton or list of strings instead of enums (see normalisation code).

- Please add badges for: pipy, license, pepy.tech pipy stats, and GitHub stars.
- The Pepy.tech badge image path is: https://static.pepy.tech/badge/litemind)
- At the end of the README, include a note explaining that the README was generated with the help of AI.

- Explain that logging is done with Arbol (http://github.com/royerlab/arbol) and it can be deactivated by setting Arbol.passthrough to True.
- Avoid hallucinating things that are not in the repository.
- Litemind _does_ _not_ have an agent class called ReActAgent, or anything 'react' related. The Agent class itself is has all the features of the ReAct framework.
- Important: If you don't know something for sure, don't make it up. If you don't know, ignore, or just say that you don't know.

Your task is to follow the instructions above and generate a README.md file for the Python repository provided below:

Directory structure:
├── LICENSE (1.45 kilobytes)
├── pyproject.toml (2.66 kilobytes)
├── TODO.md (1.51 kilobytes)
├── ANALYSIS.md (12.56 kilobytes)
├── CONTRIBUTING.md (3.40 kilobytes)
├── test_reports/
└── src/
    ├── litemind/
    │   ├── tools/
    │   │   ├── __init__.py (0 bytes)
    │   │   ├── commands/
    │   │   │   ├── export_repo.py (2.16 kilobytes)
    │   │   │   ├── __init__.py (0 bytes)
    │   │   │   ├── utils.py (732 bytes)
    │   │   │   ├── scan.py (3.08 kilobytes)
    │   │   │   └── codegen.py (10.07 kilobytes)
    │   │   └── litemind_tools.py (5.83 kilobytes)
    │   ├── apis/
    │   │   ├── combined_api.py (29.98 kilobytes)
    │   │   ├── base_api.py (21.45 kilobytes)
    │   │   ├── callbacks/
    │   │   │   ├── callback_manager.py (5.38 kilobytes)
    │   │   │   ├── base_callbacks.py (7.71 kilobytes)
    │   │   │   ├── tests/
    │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   └── test_print_callbacks.py (7.59 kilobytes)
    │   │   │   ├── __init__.py (0 bytes)
    │   │   │   └── print_callbacks.py (7.94 kilobytes)
    │   │   ├── providers/
    │   │   │   ├── google/
    │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   └── google_api.py (20.86 kilobytes)
    │   │   │   ├── __init__.py (0 bytes)
    │   │   │   ├── anthropic/
    │   │   │   │   ├── anthropic_api.py (26.02 kilobytes)
    │   │   │   │   ├── tests/
    │   │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   │   └── test_builtin_mcp_connector.py (592 bytes)
    │   │   │   │   └── __init__.py (0 bytes)
    │   │   │   ├── ollama/
    │   │   │   │   ├── ollama_api.py (17.52 kilobytes)
    │   │   │   │   ├── tests/
    │   │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   │   └── multi_image_test.py (2.43 kilobytes)
    │   │   │   │   └── __init__.py (0 bytes)
    │   │   │   └── openai/
    │   │   │       ├── __init__.py (0 bytes)
    │   │   │       └── openai_api.py (47.58 kilobytes)
    │   │   ├── tests/
    │   │   │   ├── test_apis_builtin_tools.py (5.40 kilobytes)
    │   │   │   ├── test_apis_embeddings.py (7.85 kilobytes)
    │   │   │   ├── test_feature_scanner.py (12.70 kilobytes)
    │   │   │   ├── test_apis_text_generation.py (22.55 kilobytes)
    │   │   │   ├── test_apis_multimodal_inputs.py (18.06 kilobytes)
    │   │   │   ├── __init__.py (0 bytes)
    │   │   │   ├── test_apis_documents.py (17.89 kilobytes)
    │   │   │   ├── test_feature_scanner_specifics.py (5.82 kilobytes)
    │   │   │   ├── test_apis_basics.py (7.12 kilobytes)
    │   │   │   ├── test_apis_describe.py (8.54 kilobytes)
    │   │   │   ├── test_callback_manager.py (6.06 kilobytes)
    │   │   │   ├── test_apis_generate_multimodal.py (5.95 kilobytes)
    │   │   │   └── test_callbacks_with_api.py (15.12 kilobytes)
    │   │   ├── __init__.py (0 bytes)
    │   │   ├── default_api.py (56.08 kilobytes)
    │   │   ├── exceptions.py (138 bytes)
    │   │   ├── model_features.py (8.06 kilobytes)
    │   │   └── feature_scanner.py (47.51 kilobytes)
    │   ├── __init__.py (3.70 kilobytes)
    │   ├── agent/
    │   │   ├── tools/
    │   │   │   ├── agent_tool.py (2.70 kilobytes)
    │   │   │   ├── tests/
    │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   ├── test_function_tool.py (4.46 kilobytes)
    │   │   │   │   ├── test_agent_tool.py (7.02 kilobytes)
    │   │   │   │   └── test_toolset.py (4.74 kilobytes)
    │   │   │   ├── __init__.py (0 bytes)
    │   │   │   ├── base_tool.py (2.01 kilobytes)
    │   │   │   ├── function_tool.py (4.65 kilobytes)
    │   │   │   ├── builtin_tools/
    │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   ├── web_search_tool.py (2.63 kilobytes)
    │   │   │   │   ├── mcp_tool.py (1.93 kilobytes)
    │   │   │   │   └── builtin_tool.py (357 bytes)
    │   │   │   └── toolset.py (7.68 kilobytes)
    │   │   ├── messages/
    │   │   │   ├── conversation.py (3.75 kilobytes)
    │   │   │   ├── message_block.py (4.74 kilobytes)
    │   │   │   ├── tests/
    │   │   │   │   ├── test_message.py (14.44 kilobytes)
    │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   ├── test_message_block.py (3.38 kilobytes)
    │   │   │   │   ├── test_message_append_folder.py (5.41 kilobytes)
    │   │   │   │   └── test_conversation.py (768 bytes)
    │   │   │   ├── __init__.py (0 bytes)
    │   │   │   ├── message.py (48.44 kilobytes)
    │   │   │   └── actions/
    │   │   │       ├── tool_use.py (1.64 kilobytes)
    │   │   │       ├── __init__.py (0 bytes)
    │   │   │       ├── action_base.py (423 bytes)
    │   │   │       └── tool_call.py (1.34 kilobytes)
    │   │   ├── tests/
    │   │   │   ├── test_agent.py (5.15 kilobytes)
    │   │   │   ├── __init__.py (0 bytes)
    │   │   │   ├── test_agent_advanced.py (9.71 kilobytes)
    │   │   │   └── test_agent_augmentation.py (18.60 kilobytes)
    │   │   ├── __init__.py (0 bytes)
    │   │   ├── agent.py (17.58 kilobytes)
    │   │   ├── augmentations/
    │   │   │   ├── augmentation_base.py (2.78 kilobytes)
    │   │   │   ├── information/
    │   │   │   │   ├── information.py (9.89 kilobytes)
    │   │   │   │   ├── information_base.py (2.97 kilobytes)
    │   │   │   │   ├── tests/
    │   │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   │   └── test_information.py (5.35 kilobytes)
    │   │   │   │   └── __init__.py (0 bytes)
    │   │   │   ├── vector_db/
    │   │   │   │   ├── qdrant_vector_db.py (10.74 kilobytes)
    │   │   │   │   ├── default_vector_db.py (10.11 kilobytes)
    │   │   │   │   ├── tests/
    │   │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   │   ├── test_vector_dbs.py (10.34 kilobytes)
    │   │   │   │   │   ├── test_multimodal_vector_dbs.py (12.60 kilobytes)
    │   │   │   │   │   ├── test_multimodal_vector_dbs_real_embed.py (13.37 kilobytes)
    │   │   │   │   │   └── test_in_memory_vector_db.py (2.57 kilobytes)
    │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   ├── base_vector_db.py (3.36 kilobytes)
    │   │   │   │   └── in_memory_vector_db.py (15.61 kilobytes)
    │   │   │   ├── tests/
    │   │   │   │   ├── test_vector_db_augmentations.py (4.12 kilobytes)
    │   │   │   │   ├── __init__.py (0 bytes)
    │   │   │   │   └── test_augmentation_set.py (3.83 kilobytes)
    │   │   │   ├── __init__.py (0 bytes)
    │   │   │   ├── augmentation_set.py (8.89 kilobytes)
    │   │   │   └── augmentation_default.py (3.01 kilobytes)
    │   │   └── exceptions.py (93 bytes)
    │   ├── ressources/
    │   │   ├── media_resources.py (2.22 kilobytes)
    │   │   ├── images/
    │   │   ├── tests/
    │   │   │   ├── test_media_ressources.py (1.70 kilobytes)
    │   │   │   └── __init__.py (0 bytes)
    │   │   ├── __init__.py (0 bytes)
    │   │   ├── videos/
    │   │   ├── audio/
    │   │   └── ndimages/
    │   │       └── __init__.py (0 bytes)
    │   └── media/
    │       ├── types/
    │       │   ├── media_text.py (769 bytes)
    │       │   ├── media_table.py (3.07 kilobytes)
    │       │   ├── media_audio.py (4.01 kilobytes)
    │       │   ├── media_video.py (2.94 kilobytes)
    │       │   ├── media_file.py (6.31 kilobytes)
    │       │   ├── media_image.py (4.54 kilobytes)
    │       │   ├── media_json.py (1.91 kilobytes)
    │       │   ├── __init__.py (0 bytes)
    │       │   ├── media_ndimage.py (14.24 kilobytes)
    │       │   ├── media_types.py (1.12 kilobytes)
    │       │   ├── media_document.py (2.17 kilobytes)
    │       │   ├── media_action.py (898 bytes)
    │       │   ├── media_code.py (1.30 kilobytes)
    │       │   └── media_object.py (2.30 kilobytes)
    │       ├── media_base.py (1.07 kilobytes)
    │       ├── media_default.py (927 bytes)
    │       ├── tests/
    │       │   ├── test_media_uri.py (5.36 kilobytes)
    │       │   ├── test_audio.py (4.67 kilobytes)
    │       │   ├── test_table.py (4.92 kilobytes)
    │       │   ├── test_code.py (4.87 kilobytes)
    │       │   ├── test_image.py (1.00 kilobytes)
    │       │   ├── __init__.py (0 bytes)
    │       │   ├── test_video.py (3.78 kilobytes)
    │       │   ├── test_media_base.py (880 bytes)
    │       │   ├── test_ndimage.py (8.94 kilobytes)
    │       │   ├── test_text.py (727 bytes)
    │       │   ├── test_file.py (7.29 kilobytes)
    │       │   ├── test_object.py (3.10 kilobytes)
    │       │   ├── test_media_pickle_serialisation.py (6.22 kilobytes)
    │       │   ├── test_json.py (2.36 kilobytes)
    │       │   ├── test_document.py (5.08 kilobytes)
    │       │   └── test_action.py (1.65 kilobytes)
    │       ├── __init__.py (0 bytes)
    │       ├── conversion/
    │       │   ├── tests/
    │       │   │   ├── __init__.py (0 bytes)
    │       │   │   ├── test_media_converter_basics.py (12.27 kilobytes)
    │       │   │   ├── test_media_converter_extra.py (5.24 kilobytes)
    │       │   │   ├── test_media_converter_rules.py (4.44 kilobytes)
    │       │   │   └── test_media_converter_per_type.py (16.46 kilobytes)
    │       │   ├── converters/
    │       │   │   ├── document_converter_pymupdf.py (5.49 kilobytes)
    │       │   │   ├── code_converter.py (897 bytes)
    │       │   │   ├── media_converter_delegated_callables.py (3.87 kilobytes)
    │       │   │   ├── ndimage_converter.py (1.04 kilobytes)
    │       │   │   ├── audio_converter_whisper_local.py (1.83 kilobytes)
    │       │   │   ├── document_converter_txt.py (2.03 kilobytes)
    │       │   │   ├── __init__.py (0 bytes)
    │       │   │   ├── video_converter_ffmpeg.py (4.51 kilobytes)
    │       │   │   ├── file_converter.py (990 bytes)
    │       │   │   ├── json_converter.py (896 bytes)
    │       │   │   ├── document_converter_python_minify.py (5.76 kilobytes)
    │       │   │   ├── base_converter.py (1.74 kilobytes)
    │       │   │   ├── object_converter.py (1.01 kilobytes)
    │       │   │   ├── document_converter_docling.py (6.37 kilobytes)
    │       │   │   └── table_converter.py (906 bytes)
    │       │   ├── __init__.py (0 bytes)
    │       │   └── media_converter.py (15.22 kilobytes)
    │       └── media_uri.py (5.24 kilobytes)
    └── notebooks/
        └── demo.ipynb (23.93 kilobytes)


==========
Text File: ANALYSIS.md
==========


Document: ANALYSIS.md
Type: text
Extension: md
Number of pages: 1



- [&lt;RawText children='Project Purpose:'&gt;]
- [&lt;RawText children='Strengths:'&gt;]
    - [&lt;RawText children='Unified API:'&gt;]
    - [&lt;RawText children='Multimodal Input Support:'&gt;]
    - [&lt;RawText children='Agentic Framework:'&gt;]
    - [&lt;RawText children='Tool Integration:'&gt;]
    - [&lt;RawText children='Extensible Architecture:'&gt;]
    - [&lt;RawText children='Command-Line Tools:'&gt;]
    - [&lt;RawText children='Good Documentation:'&gt;]
    - [&lt;RawText children='Testing:'&gt;]
- [&lt;RawText children='Weaknesses:'&gt;]
    - [&lt;RawText children='Error Handling:'&gt;]
    - [&lt;RawText children='Token Management:'&gt;]
    - [&lt;RawText children='API Key Management:'&gt;]
    - [&lt;RawText children='Performance:'&gt;]
    - [&lt;RawText children='Failing Tests:'&gt;]
- [&lt;RawText children='Key Findings:'&gt;]
- [&lt;RawText children='Technical Sophistication:'&gt;]
- [&lt;RawText children='Innovation:'&gt;]

- [&lt;RawText children='Code Quality:'&gt;]
    - The code appears to be well-structured and organized, with a clear separation of concerns.
    - Readability is generally good, with meaningful variable names and comments.
    - The project uses
    - The code adheres to PEP-8 guidelines, as indicated by the
    - The use of type hints improves code readability and maintainability.
- [&lt;RawText children='Test Coverage and Quality:'&gt;]
    - The project has a comprehensive test suite, as evidenced by the presence of a
    - The
    - The test suite covers a wide range of functionalities, including text generation, image generation, audio
    - However, the
- [&lt;RawText children='Dependency Management:'&gt;]
    - The
    - The use of
    - Optional dependencies are well-defined, allowing users to install only the necessary dependencies for their use
- [&lt;RawText children='Technical Debt Identification:'&gt;]
    - The
    - The failing tests in
- [&lt;RawText children='Performance Considerations:'&gt;]
    - Performance considerations, such as caching and optimization of API calls, are not explicitly addressed.
    - The use of

- [&lt;RawText children='Repository Structure Evaluation:'&gt;]
    - The repository has a well-organized structure, with clear separation of concerns.
    - The
    - The
    - The
    - The
    - The
    - The
- [&lt;RawText children='Module Organization and Interfaces:'&gt;]
    - The modules are well-organized, with clear interfaces.
    - The
    - Concrete API implementations (e.g.,
    - The
    - The
- [&lt;RawText children='Class Hierarchy and Design Patterns:'&gt;]
    - The project uses inheritance to implement different API providers.
    - The
    - The
    - The use of the strategy pattern is evident in the different API implementations.
- [&lt;RawText children='Concurrency Model:'&gt;]
    - The project does not explicitly mention a concurrency model.
- [&lt;RawText children='Error Handling Approach:'&gt;]
    - The project uses custom exception classes (e.g.,
    - Error handling could be improved, particularly in the API wrapper layer.

- [&lt;RawText children='Adherence to SOLID Principles:'&gt;]
    - [&lt;RawText children='Single Responsibility Principle:'&gt;]
    - [&lt;RawText children='Open/Closed Principle:'&gt;]
    - [&lt;RawText children='Liskov Substitution Principle:'&gt;]
    - [&lt;RawText children='Interface Segregation Principle:'&gt;]
    - [&lt;RawText children='Dependency Inversion Principle:'&gt;]
- [&lt;RawText children='API Design and Usability:'&gt;]
    - The API is designed to be user-friendly and intuitive.
    - The
    - The
    - The examples in the
- [&lt;RawText children='Extensibility and Maintainability:'&gt;]
    - The project is designed for extensibility, allowing for easy addition of new LLM providers and tool integrations.
    - The use of inheritance and abstract classes promotes code reuse and maintainability.
    - The clear separation of concerns makes the code easier to understand and modify.
- [&lt;RawText children='Configuration and Environment Management:'&gt;]
    - The project relies on environment variables for API keys.
    - The
    - The use of environment variables for API keys is a common practice, but consider using a more secure key
- [&lt;RawText children='Security Considerations:'&gt;]
    - The project relies on environment variables for API keys.
    - Consider using a more secure key management solution in production environments.

- [&lt;RawText children='Model Architecture Choices:'&gt;]
    - The project does not specify the model architectures used. It provides a wrapper API around existing LLM APIs.
- [&lt;RawText children='Training/Inference Pipeline Design:'&gt;]
    - The project focuses on the inference pipeline, providing a unified API for interacting with different LLM
    - The
    - The
- [&lt;RawText children='Optimization Techniques:'&gt;]
    - The project does not explicitly mention any optimization techniques.
- [&lt;RawText children='Novel Approaches or Algorithms:'&gt;]
    - The project does not introduce any novel algorithms or architectures.
- [&lt;RawText children='Comparison with State-of-the-Art Alternatives:'&gt;]
    - The project provides a unified API for interacting with various LLM providers, which is a common approach in the
    - The ReAct agent is a well-known and effective approach for building agentic AI applications.

- [&lt;RawText children='Prioritized List of Actionable Improvements:'&gt;]
    1. [&lt;RawText children='Address Failing Tests:'&gt;]
    2. [&lt;RawText children='Improve Error Handling:'&gt;]
    3. [&lt;RawText children='Implement Token Management:'&gt;]
    4. [&lt;RawText children='Enhance API Key Management:'&gt;]
    5. [&lt;RawText children='Add Documentation:'&gt;]
    6. [&lt;RawText children='Performance Optimization:'&gt;]
- [&lt;RawText children='Specific Code Examples of Problematic Patterns and Suggested Refactorings:'&gt;]
    - [&lt;RawText children='Failing Tests:'&gt;]
    - [&lt;RawText children='Error Handling:'&gt;]
    - [&lt;RawText children='Token Management:'&gt;]
- [&lt;RawText children='Architecture Evolution Suggestions:'&gt;]
    - [&lt;RawText children='Modularize API Implementations:'&gt;]
    - [&lt;RawText children='Implement Streaming Support:'&gt;]
    - [&lt;RawText children='Add Support for More Media Types:'&gt;]
- [&lt;RawText children='Performance Optimization Opportunities:'&gt;]
    - [&lt;RawText children='Caching:'&gt;]
    - [&lt;RawText children='Asynchronous Operations:'&gt;]
    - [&lt;RawText children='Batching:'&gt;]

The Litemind project is a promising Python library for developing multimodal agentic AI applications. It has a solid foundation, a well-organized structure, and a comprehensive test suite. By addressing the identified weaknesses and implementing the recommended improvements, the project can become even more powerful and user-friendly. The focus on a unified API and agentic framework makes it a valuable tool for developers working with LLMs.

==========
Text File: CONTRIBUTING.md
==========


Document: CONTRIBUTING.md
Type: text
Extension: md
Number of pages: 1

Here's a comprehensive CONTRIBUTING.md that follows best practices for open source Python projects:
```

First off, thank you for considering contributing to LiteMind!

1. Fork the repository
2. Create a new branch for your feature/fix: `git checkout -b feature-name`
3. Make your changes
4. Run tests: `pytest`
5. Push to your fork: `git push origin feature-name`
6. Submit a Pull Request

1. Clone your fork:
```bash
git clone https://github.com/royerlab/litemind.git
cd litemind
```
1. Create a virtual environment:
```
python -m venv venv
source venv/bin/activate  # On Windows: `venv\Scripts\activate`
```
1. Install development dependencies:
```
pip install -e ".[dev]"
```

We use several tools to maintain code quality:
- [&lt;RawText children='Black'&gt;]
- [&lt;RawText children='isort'&gt;]
- [&lt;RawText children='flake8'&gt;]
- [&lt;RawText children='mypy'&gt;]
Run the following before committing:
```
isort .
black .
flake8 .
mypy src/litemind
```
And fix any issues that arise in the files you've modified.
Better yet, set up pre-commit hooks:
```
pre-commit install
```

- Write tests for new features using pytest
- Ensure all tests pass:
- Maintain or improve code coverage

- Use Numpy-style docstrings
- Update documentation when adding/modifying features
- Keep the README.md up to date
Example docstring:
```
def function_name(param1: str, param2: int) -> bool:
    """
    Short description of function.
    Longer description if needed.
    Parameters

    param1 : str
        Description of param1
    param2 : int
        Description of param2
    Returns

    bool
        Description of return value
    Raises

    ValueError
        Description of when this error occurs
    """
```

1. Update documentation if needed
2. Add tests for new features
3. Ensure CI pipeline passes
4. Get review from at least one maintainer
5. Squash commits if requested

- main
- develop
- Feature branches:
- Bug fixes:
- Release branches:

Follow conventional commits:
```
feat: add new feature
fix: correct bug
docs: update documentation
style: formatting changes
refactor: code restructuring
test: add/modify tests
chore: maintenance tasks
```

1. Update version in pyproject.toml
2. Update CHANGELOG.md
3. Create release branch
4. Run full test suite
5. Create GitHub release
6. Deploy to PyPI

- Open an issue for bugs
- Start a discussion for feature requests
- Join our community chat (if applicable)
- Tag issues appropriately (

Please read our Code of Conduct. We expect all contributors to adhere to it.

By contributing, you agree that your contributions will be licensed under the BSD-3-Clause License.
This CONTRIBUTING.md:
- Sets clear expectations
- Makes it easy for new contributors to get started
- Provides specific commands and examples
- Covers all major aspects of contribution
- Establishes code quality standards
- Defines the development workflow
Would you like me to elaborate on any particular section?

==========
Text File: LICENSE
==========

LICENSE
```royer/workspace/python/litemind/license
Copyright (c) 2023, Loic A. Royer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.
* Neither the name of copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```

==========
Text File: TODO.md
==========


Document: TODO.md
Type: text
Extension: md
Number of pages: 1


- [x] Setup a readme with a quick start guide.
- [x] setup continuous integration and pipy deployment.
- [x] Improve document conversion (page per page text and video interleaving + whole page images)
- [x] Cleanup structured output with tool usage
- [x] Implement streaming callbacks
- [x] Improve folder/archive conversion: add ascii folder tree
- [x] Reorganise media files used for testing into a single media folder
- [x] Improve logging with arbol, with option to turn off.
- [x] Use specialised libraries for document type identification
- [x] Cleanup the document conversion code.
- [x] Add support for adding nD images to messages.
- [x] Automatic feature support discovery for models (which models support images as input, reasoning, etc...)
- [x] Add support for OpenAI's new 'Response' API.
- [x] Add support for builtin tools: Web search and MCP protocol.
- [ ] Add webui functionality for agents using Reflex.
- [ ] Video conversion temporal sampling should adapt to the video length, short videos should have more frames...
- [ ] RAG ingestion code for arbitrary digital objects: folders, pdf, images, urls, etc...
- [ ] Add more support for MCP protocol beyond built-in API support.
- [ ] Use the faster pybase64 for base64 encoding/decoding.
- [ ] Deal with message sizes in tokens sent to models
- [ ] Improve vendor api robustness features such as retry call when server errors, etc...
- [ ] Improve and uniformize exception handling
- [ ] Implement 'brainstorming' mode for text generation, possibly with API fusion.

==========
Text File: pyproject.toml
==========

pyproject.toml
```toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "litemind"
dynamic = ["version"]
description = "A wrapper API around LLM APIs and agentic AI framework"
authors = [{ name = "Loic A. Royer" }]
license = "BSD-3-Clause"        # SPDX
readme = "README.md"
requires-python = ">=3.9"
keywords = ["llm", "ai", "agents", "wrapper"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: BSD License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
]
dependencies = [
    "openai>=1.86.0",
    "ollama>=0.5.1",
    "anthropic>=0.53.0",
    "google-generativeai",
    "pillow",
    "arbol",
    "ansicolors",
    "json-repair",
    "tiktoken",
    "pydantic",
    "pandas",
    "numpy",
    "chardet",
    "pyyaml",
    "absl-py",
    "scipy",
    "filetype",
    "python-magic",
]
[project.optional-dependencies]
dev = [
    "hatch",
    "pytest",
    "pytest-html",
    "pytest-cov",
    "pytest-md-report",
    "black[jupyter]",
    "flake8",
    "mypy",
    "isort",
    "pypdf",
]
rag = ["fastembed", "chromadb", "qdrant-client"]
whisper = ["openai-whisper"]
documents = ["pymupdf", "pymupdf4llm", "openpyxl", "docling", "python-minifier"]
tables = ["tabulate"]
videos = ["ffmpeg-python", "imageio[ffmpeg]"]
audio = ["soundfile"]
[project.urls]
Homepage = "https://github.com/royerlab/litemind"
Issues = "https://github.com/royerlab/litemind/issues"
[tool.hatch.version]
path = "src/litemind/__init__.py"

[tool.hatch.build.targets.sdist]
ignore-vcs = true
include = ["**", "README.md", "LICENSE"]
exclude = [
    "**/tests/**",
    "**/.DS_Store",
    "*.egg-info",
    "**/sandbox/**",
    "src/litemind/ressources/**",
]

[tool.hatch.build.targets.wheel]
packages = ["src/litemind"]
[project.scripts]
litemind = "litemind.tools.litemind_tools:main"
[tool.hatch.envs.hatch-test]
addopts = "--html=reports/report.html --self-contained-html --cov=src --cov-report=html:reports/coverage --md-report=reports/report.md"
default-args = ["src/"]
exclude = ["sandbox"]
extra-dependencies = [
    "qdrant-client", "chromadb", "openai-whisper", "pymupdf",
    "pymupdf4llm", "openpyxl", "docling", "pandas", "numpy",
    "chardet", "tabulate", "fastembed", "ffmpeg-python",
    "imageio[ffmpeg]", "pytest-html", "pytest-cov",
    "pytest-md-report", "pypdf", "soundfile", "filetype",
    "python-magic", "python-minifier"
]
```










==========
Prog. Lang. Code File: agent.py
Size: 17.58 kilobytes
==========

agent.py
```py
_D='before_query'
_C='system'
_B='The model does not support tools'
_A=None
from typing import List,Optional,Sequence,Union
from arbol import aprint,asection
from litemind.agent.augmentations.augmentation_base import AugmentationBase
from litemind.agent.augmentations.augmentation_set import AugmentationSet
from litemind.agent.augmentations.information.information_base import InformationBase
from litemind.agent.messages.conversation import Conversation
from litemind.agent.messages.message import Message
from litemind.agent.tools.base_tool import BaseTool
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import BaseApi
from litemind.apis.model_features import ModelFeatures
class Agent:
	def __init__(A,api,name='Agent',model_name=_A,model_features=_A,temperature=.0,toolset=_A,augmentation_set=_A,augmentation_k=5,augmentation_context_position=_D,messages_stdout_enabled=False,**I):
		F=toolset;E=model_name;D=augmentation_context_position;C=api;B=model_features;A.api=C;B=ModelFeatures.normalise(B)
		if B is not _A:
			if E:raise ValueError('You cannot provide both model and model_features')
			if ModelFeatures.TextGeneration not in B:B=list(B)+[ModelFeatures.TextGeneration]
			if ModelFeatures.Tools not in B:B=list(B)+[ModelFeatures.Tools]
			A.model=C.get_best_model(features=B)
		else:A.model=E or C.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Tools])
		if A.model is _A:raise ValueError('No model was found with the required features')
		if not A.api.has_model_support_for(model_name=A.model,features=ModelFeatures.TextGeneration):raise ValueError('The model does not support text generation')
		G=A.api.has_model_support_for(model_name=A.model,features=ModelFeatures.Tools)
		if F is not _A and not G:raise ValueError(_B)
		A.temperature=temperature;A.toolset=F or(ToolSet()if G else _A);A.name=name;A._agent_kwargs=I;A.augmentation_set=augmentation_set or AugmentationSet();A.augmentation_k=augmentation_k;H=[_D,_C]
		if D not in H:raise ValueError(f"Invalid augmentation_context_position: {D}. Valid options are: {H}")
		A.augmentation_context_position=D;A.conversation=Conversation();A.messages_stdout_enabled=messages_stdout_enabled
	def append_system_message(B,message):
		A=message
		if isinstance(A,str):A=Message(role=_C,text=A)
		B.conversation.system_messages.append(A)
	def add_tool(A,tool):
		if A.toolset is _A:raise ValueError(_B)
		A.toolset.add_tool(tool)
	def remove_tool(A,name):
		if A.toolset is _A:raise ValueError(_B)
		return A.toolset.remove_tool(name)
	def clear_tools(A):
		if A.toolset is _A:raise ValueError(_B)
		A.toolset=ToolSet()
	def add_augmentation(A,augmentation,k=5,threshold=.0):A.augmentation_set.add_augmentation(augmentation,k=k,threshold=threshold)
	def remove_augmentation(A,name):return A.augmentation_set.remove_augmentation(name)
	def clear_augmentations(A):A.augmentation_set=AugmentationSet()
	def list_augmentations(A):return A.augmentation_set.list_augmentations()
	def _retrieve_relevant_documents(B,query_text):
		C=query_text
		with asection(f"Retrieving relevant informations for query: '{C[:100]}...'"):
			A=B.augmentation_set.search_combined(C,k=B.augmentation_k)
			with asection(f"Retrieved {len(A)} informations from augmentation set:"):
				for D in A:aprint(f"Document ID: {D.id}, Score: {D.score:.4f}")
			return A
	def _add_context_to_conversation(B,query_message,documents,max_doc_length=1000):
		F='augmentation';E=documents
		if not E:return
		if B.augmentation_context_position==_D:A=Message(role='user');G=B.conversation.standard_messages.index(query_message);B.conversation.standard_messages.insert(G,A)
		elif B.augmentation_context_position==_C:A=Message(role=_C);B.conversation.system_messages.append(A)
		else:raise ValueError(f"Invalid augmentation_context_position: {B.augmentation_context_position}")
		A.append_text('Additional context information:\n\n')
		for(H,C)in enumerate(E):
			D='';D+=f"--- Document {H+1} "
			if C.score is not _A:D+=f"(Relevance: {C.score:.4f}) "
			if F in C.metadata:D+=f"[Source: {C.metadata[F]}] "
			D+='---\n';A.append_text(D);A+=C.to_message_block()
		with asection(f"Added {len(E)} informations to conversation, context message:"):aprint(str(A))
	def __getitem__(A,item):return A.conversation[item]
	def __len__(A):return len(A.conversation)
	def __iadd__(A,other):A.conversation.append(other);return A
	def __call__(A,*G,**D):
		A._prepare_call(*G,**D);C=A.conversation.get_last_message()
		with asection(f"Calling agent: '{A.name}'"):
			with asection('API and model:'):aprint(f"API: {A.api.__class__.__name__}");aprint(f"Model: {A.model}")
			if A.toolset:
				with asection('Available tools'):
					if len(A.toolset)>0:
						for H in A.toolset:aprint(H.pretty_string())
					else:aprint('No tools available')
			with asection('Available augmentations'):
				E=A.augmentation_set.list_augmentations()
				if E:
					for I in E:aprint(f"{I.name}")
				else:aprint('No augmentations available')
			if A.conversation:
				with asection(f"Conversation report:"):aprint(f"Number of messages currently in conversation: {len(A.conversation)}")
			if A.messages_stdout_enabled:
				with asection('Last message in conversation:'):aprint(C)
			if len(A.augmentation_set)>0 and C:
				J=C.to_plain_text();F=A._retrieve_relevant_documents(J)
				if F:A._add_context_to_conversation(C,F)
			B=A.api.generate_text(model_name=A.model,messages=A.conversation.get_all_messages(),temperature=A.temperature,toolset=A.toolset,**D)
			with asection(f"Response:"):
				if B is _A:aprint('No response received from the API. Probably an error occurred.')
				else:aprint(f"Number of messages in response: {len(B)}")
			if A.messages_stdout_enabled:
				with asection('Reponse:'):
					for K in B:aprint(K)
		A.conversation.extend(B);return B
	def _prepare_call(E,*F,**B):
		L='role';J='text';I='message';H='conversation';C=[];K=B[L]if L in B else'user'
		if F:
			D=F[0]
			if isinstance(D,Conversation):E.conversation+=D;C.extend(D.get_all_messages())
		if H in B:
			D=B[H];del B[H]
			if isinstance(D,Conversation):E.conversation+=D;C.extend(D.get_all_messages())
			else:raise ValueError('conversation must be a Conversation object')
		if F:
			for A in F:
				if isinstance(A,Message):E.conversation.append(A);C.append(A)
		if I in B:
			A=B[I];del B[I]
			if isinstance(A,Message):E.conversation.append(A);C.append(A)
			else:raise ValueError('message must be a Message object')
		if F:
			for G in F:
				if isinstance(G,str):A=Message(role=K);A.append_text(G);E.conversation.append(A);C.append(A)
		if J in B:
			G=B[J];del B[J]
			if isinstance(G,str):A=Message(role=K,text=G);E.conversation.append(A);C.append(A)
			else:raise ValueError('text must be a string')
		return C
	def __repr__(A):return f"{A.name}(model={A.model}, api={A.api.__class__.__name__})"
	def __str__(A):return A.__repr__()
```

==========
Prog. Lang. Code File: exceptions.py
Size: 93 bytes
==========

exceptions.py
```py
class MessageError(Exception):0
```




==========
Prog. Lang. Code File: augmentation_base.py
Size: 2.78 kilobytes
==========

augmentation_base.py
```py
from abc import ABC,abstractmethod
from typing import Iterator,List,Optional,Union
from pydantic import BaseModel
from litemind.agent.augmentations.information.information import Information
from litemind.media.media_base import MediaBase
class AugmentationBase(ABC):
	def __init__(A,name,description=None):A.name=name;A.description=description or f"{name} Augmentation"
	@abstractmethod
	def get_relevant_informations(self,query,k=5,threshold=.0):0
	def get_relevant_informations_iterator(A,query,k=5,threshold=.0):0
	def __repr__(A):return f"{A.__class__.__name__}(name='{A.name}')"
	def __str__(A):return A.name
```

==========
Prog. Lang. Code File: augmentation_default.py
Size: 3.01 kilobytes
==========

augmentation_default.py
```py
from typing import Iterator,Optional,Union
from pydantic import BaseModel
from litemind.agent.augmentations.augmentation_base import AugmentationBase
from litemind.agent.augmentations.information.information import Information
from litemind.media.media_base import MediaBase
from litemind.media.types.media_object import Object
from litemind.media.types.media_text import Text
class AugmentationDefault(AugmentationBase):
	def __init__(A,name,description=None):super().__init__(name,description)
	@staticmethod
	def _normalize_query(query):
		A=query
		if isinstance(A,str):return Text(A)
		elif isinstance(A,BaseModel):return Object(A)
		elif isinstance(A,MediaBase):return A
		elif isinstance(A,Information):B=A.media;return B
		else:raise ValueError(f"Unsupported query type: {type(A)}. Supported types are: str, BaseModel, MediaBase, Information.")
	def get_relevant_informations_iterator(A,query,k=5,threshold=.0):
		B=A._normalize_query(query)
		for C in A.get_relevant_informations(B,k=k,threshold=threshold):yield C
```

==========
Prog. Lang. Code File: augmentation_set.py
Size: 8.89 kilobytes
==========

augmentation_set.py
```py
_A=None
from typing import Dict,List,Optional,Union
from arbol import aprint
from pydantic import BaseModel
from litemind.agent.augmentations.augmentation_base import AugmentationBase
from litemind.agent.augmentations.information.information import Information
from litemind.agent.augmentations.information.information_base import InformationBase
from litemind.media.media_base import MediaBase
class AugmentationSet:
	def __init__(A):A.augmentations=[];A.k_dict={};A.threshold_dict={}
	def add_augmentation(A,augmentation,k=5,threshold=.0):B=augmentation;A.augmentations.append(B);A.k_dict[B]=k;A.threshold_dict[B]=threshold
	def get_augmentation(B,name):
		for A in B.augmentations:
			if A.name==name:return A
	def list_augmentations(A):return A.augmentations
	def augmentation_names(A):return[A.name for A in A.augmentations]
	def remove_augmentation(A,name):
		for(C,B)in enumerate(A.augmentations):
			if B.name==name:del A.augmentations[C];del A.k_dict[B];del A.threshold_dict[B];return True
		return False
	def search_all(C,query,k=_A,threshold=_A):
		B={}
		for A in C.augmentations:
			try:D=C._search_raw(A,k,query,threshold);B[A.name]=D
			except Exception as E:aprint(f"Error searching augmentation {A.name}: {E}");B[A.name]=[]
		return B
	def search_combined(B,query,k=5,threshold=.0):
		A=[]
		for C in B.augmentations:
			try:D=B._search_raw(C,k,query,threshold);A.extend(D)
			except Exception as E:aprint(f"Error searching augmentation {C.name}: {E}")
		A.sort(key=lambda x:x.score if x.score is not _A else 0,reverse=True);return A[:k]
	def _search_raw(C,augmentation,k,query,threshold):
		A=augmentation;E=k or C.k_dict.get(A);F=threshold or C.threshold_dict.get(A);D=A.get_relevant_informations(query,k=E,threshold=F)
		for B in D:
			if B.metadata is _A:B.metadata={}
			B.metadata['augmentation']=A.name
		return D
	def __getitem__(A,item):return A.augmentations[item]
	def __len__(A):return len(A.augmentations)
	def __iter__(A):return iter(A.augmentations)
	def __contains__(B,item):
		A=item
		if isinstance(A,str):return any(B.name==A for B in B.augmentations)
		return A in B.augmentations
	def __str__(A):return f"AugmentationSet({[A.name for A in A.augmentations]})"
	def __repr__(A):return A.__str__()
```




==========
Prog. Lang. Code File: information.py
Size: 9.89 kilobytes
==========

information.py
```py
_A=None
import uuid
from typing import Any,Callable,List,Optional,Type
from numpy import mean
from pydantic import BaseModel
from litemind.agent.augmentations.information.information_base import InformationBase
from litemind.agent.messages.message_block import MessageBlock
from litemind.apis.base_api import BaseApi
from litemind.media.media_base import MediaBase
from litemind.utils.pickle_serialisation import PickleSerializable
class Information(InformationBase,PickleSerializable):
	def __init__(A,media,summary=_A,metadata=_A,id=_A,score=_A,parent=_A,children=_A):
		D=metadata;C=media;B=children
		if C is _A and B is _A:raise ValueError('Cannot create an Information with no media and no children.')
		if C is not _A and B is not _A:raise ValueError('Cannot create an Information with both media and children.')
		A.media=C;A.children=B if B is not _A else[];A.summary=summary;A._metadata=D if D is not _A else{};A._id=id or str(uuid.uuid4());A._score=score;A.embedding=_A;A.parent=parent
		for E in A.children:E.parent=A
	def has_type(A,media_type):return isinstance(A.media,media_type)
	@property
	def type(self):return type(self.media)
	@property
	def content(self):return self.media.get_content()
	@property
	def metadata(self):return self._metadata
	@property
	def id(self):return self._id
	@property
	def score(self):return self._score
	@score.setter
	def score(self,value):self._score=value
	def set_id(A,value):A._id=value
	def add_child(A,document):B=document;B.parent=A;A.children.append(B)
	def compute_embedding(A,embedding_function=_A):
		B=embedding_function
		if B is _A:from litemind import CombinedApi as D;E=D();B=lambda text:E.embed_texts([text])[0]
		if A.embedding is not _A:return A.embedding
		if not A.children:A.embedding=B(A.content);return list(A.embedding)
		for C in A.children:
			if C.embedding is _A:C.compute_embedding(B)
		F=[A.embedding for A in A.children]+[B(A.content)];A.embedding=mean(F,axis=0).tolist();return A.embedding
	def get_all_informations(A):
		B=[A]
		for C in A.children:B.extend(C.get_all_informations())
		return B
	def summarize(A,summarization_function=_A,model=_A,api=_A,temperature=.0):
		D=model;C=summarization_function;B=api
		if C is _A:from litemind import CombinedApi as G;from litemind.agent.messages.message import Message as E;B=B or G();H=E(role='system',text='You are a helpful assistant that summarizes informations.');F=E(role='user',text=f"Please summarize the following document in a concise way.");F.append_media(A.media);I=B.generate_text(messages=[H,F],temperature=temperature,model_name=D);A.summary=I[0].to_plain_text()
		else:
			if B is not _A or D is not _A:raise ValueError('Cannot provide api or model if summarization_function is provided.')
			A.summary=C(A.content)
		return A.summary
	def to_message_block(A):return MessageBlock(role='user',media=A.media,metadata=A.metadata,id=A.id,score=A.score)
	def __copy__(A):return Information(media=A.media,summary=A.summary,metadata=A.metadata,id=A.id,score=A.score)
	def __contains__(A,item):return item in A.media
	def __str__(A):return str(A.media)
	def __hash__(A):return hash(A.media)
	def __eq__(A,other):return A.media==other.media
	def __ne__(A,other):return not A==other
	def __len__(A):return len(A.media)
```

==========
Prog. Lang. Code File: information_base.py
Size: 2.97 kilobytes
==========

information_base.py
```py
from abc import ABC,abstractmethod
from typing import Callable,List,Optional,Type
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.media_base import MediaBase
class InformationBase(ABC):
	@property
	@abstractmethod
	def content(self):0
	@property
	@abstractmethod
	def type(self):0
	@property
	@abstractmethod
	def metadata(self):0
	@abstractmethod
	def has_type(self,media_type):0
	@property
	@abstractmethod
	def id(self):0
	@property
	@abstractmethod
	def score(self):0
	@abstractmethod
	def compute_embedding(self,embedding_function=None):0
	@abstractmethod
	def to_message_block(self):0
	@abstractmethod
	def __str__(self):0
	@abstractmethod
	def __len__(self):0
```




==========
Prog. Lang. Code File: test_information.py
Size: 5.35 kilobytes
==========

test_information.py
```py
_D='Child 2'
_C='Child 1'
_B='Test summary'
_A='Test content'
import uuid
from typing import Any,List
import numpy as np,pytest
from litemind.agent.augmentations.information.information import Information
from litemind.media.media_default import MediaDefault
class MockMedia(MediaDefault):
	def __init__(A,content):A._content=content
	def get_content(A):return A._content
	def __str__(A):return str(A._content)
def simple_embedding_function(text):return[float(len(text)),.5,.25]
def simple_summarization_function(content):A=content;B=A.split();return B[0]+'...'if len(B)>1 else A
def test_init_with_all_parameters():D='test_value';C='test_key';E=MockMedia(_A);B=str(uuid.uuid4());A=Information(media=E,summary=_B,metadata={C:D},id=B,score=.9);assert A.content==_A;assert A.summary==_B;assert A.metadata=={C:D};assert A.id==B;assert A.score==.9
def test_content_property():A='Another content';B=MockMedia(A);C=Information(media=B);assert C.content==A
def test_id_property():A='custom-doc-id';B=Information(media=MockMedia('Content'),id=A);assert B.id==A
def test_score_property():A=Information(media=MockMedia('Score test'));assert A.score is None;A.score=.8;assert A.score==.8
def test_add_child():A=Information(media=MockMedia('Parent'));B=Information(media=MockMedia('Child'));A.add_child(B);assert B in A.children;assert B.parent==A
def test_compute_embedding_no_children():C='Short text';A=Information(media=MockMedia(C));B=A.compute_embedding(embedding_function=simple_embedding_function);assert B==simple_embedding_function(C);assert A.embedding==B
def test_compute_embedding_with_children():
	B='Parent info';A=Information(media=MockMedia(B));C=Information(media=MockMedia(_C));D=Information(media=MockMedia(_D));A.add_child(C);A.add_child(D);E=A.compute_embedding(embedding_function=simple_embedding_function);F=np.mean([simple_embedding_function(_C),simple_embedding_function(_D),simple_embedding_function(B)],axis=0).tolist()
	for(G,H)in zip(E,F):assert pytest.approx(G)==H
def test_summarize_custom_function():A=Information(media=MockMedia('This is a test document with multiple words'));B=A.summarize(summarization_function=simple_summarization_function);assert B=='This...'
@pytest.mark.skip(reason='Requires external API call')
def test_summarize_default_function():A=Information(media=MockMedia('Test content for default summarizer'));B=A.summarize();assert B is not None
def test_get_all_informations():B=Information(media=MockMedia('Root'));C=Information(media=MockMedia(_C));D=Information(media=MockMedia(_D));E=Information(media=MockMedia('Grandchild'));B.add_child(C);B.add_child(D);C.add_child(E);A=B.get_all_informations();assert len(A)==4;assert B in A;assert C in A;assert D in A;assert E in A
def test_copy():B=Information(media=MockMedia('Copy test content'),summary=_B,metadata={'key':'value'},id='copy-id',score=.95);A=B.__copy__();assert A is not B;assert A.media.get_content()==B.media.get_content();assert A.summary==B.summary;assert A.metadata==B.metadata;assert A.id==B.id;assert A.score==B.score;assert A.parent is None;assert A.children==[]
def test_contains():A=Information(media=MockMedia(_A));assert'Test'in A;assert'content'in A;assert'random'not in A
def test_str():A='String representation';B=Information(media=MockMedia(A));assert str(B)==A
def test_hash():B='Same content';A=Information(media=MockMedia(B));C=Information(media=MockMedia(B));D=Information(media=MockMedia('Different content'));assert hash(A)==hash(C);assert hash(A)!=hash(D)
def test_eq():B='Equal content';A=Information(media=MockMedia(B));C=Information(media=MockMedia(B));D=Information(media=MockMedia('Not equal'));assert A==C;assert A!=D
def test_ne():B='Different check';A=Information(media=MockMedia(B));C=Information(media=MockMedia(B));D=Information(media=MockMedia('Totally different'));assert not A!=C;assert A!=D
```




==========
Prog. Lang. Code File: test_augmentation_set.py
Size: 3.83 kilobytes
==========

test_augmentation_set.py
```py
_E='test query'
_D='nonexistent'
_C='dummy'
_B='dummy2'
_A='dummy1'
from litemind.agent.augmentations.augmentation_base import AugmentationBase
from litemind.agent.augmentations.augmentation_set import AugmentationSet
from litemind.agent.augmentations.information.information import Information
class DummyAugmentation(AugmentationBase):
	def __init__(A,name):A.name=name
	def get_relevant_informations(A,query,k=5,threshold=.0):return[Information(media=f"Result {A} for {query}",metadata={'score':A})for A in range(k)]
def test_add_augmentation():A=AugmentationSet();B=DummyAugmentation(name=_C);A.add_augmentation(B);assert len(A)==1;assert A.get_augmentation(_C)==B
def test_get_augmentation():B=DummyAugmentation(name=_C);A=AugmentationSet();A.add_augmentation(B);assert A.get_augmentation(_C)==B;assert A.get_augmentation(_D)is None
def test_list_augmentations():B=DummyAugmentation(name=_A);C=DummyAugmentation(name=_B);A=AugmentationSet();A.add_augmentation(B);A.add_augmentation(C);assert A.list_augmentations()==[B,C]
def test_augmentation_names():B=DummyAugmentation(name=_A);C=DummyAugmentation(name=_B);A=AugmentationSet();A.add_augmentation(B);A.add_augmentation(C);assert A.augmentation_names()==[_A,_B]
def test_remove_augmentation():B=DummyAugmentation(name=_C);A=AugmentationSet();A.add_augmentation(B);assert A.remove_augmentation(_C)is True;assert len(A)==0;assert A.remove_augmentation(_D)is False
def test_search_all():C=DummyAugmentation(name=_A);D=DummyAugmentation(name=_B);B=AugmentationSet();B.add_augmentation(C);B.add_augmentation(D);A=B.search_all(_E,k=3);assert _A in A;assert _B in A;assert len(A[_A])==3;assert len(A[_B])==3
def test_search_combined():C=DummyAugmentation(name=_A);D=DummyAugmentation(name=_B);A=AugmentationSet();A.add_augmentation(C);A.add_augmentation(D);B=A.search_combined(_E,k=5);assert len(B)==5;assert all(A.metadata['augmentation']in[_A,_B]for A in B)
def test_len():B=DummyAugmentation(name=_C);A=AugmentationSet();A.add_augmentation(B);assert len(A)==1
def test_iter():B=DummyAugmentation(name=_A);C=DummyAugmentation(name=_B);A=AugmentationSet();A.add_augmentation(B);A.add_augmentation(C);D=[A for A in A];assert D==[B,C]
def test_contains():B=DummyAugmentation(name=_C);A=AugmentationSet();A.add_augmentation(B);assert _C in A;assert B in A;assert _D not in A
def test_str_repr():B="AugmentationSet(['dummy1', 'dummy2'])";C=DummyAugmentation(name=_A);D=DummyAugmentation(name=_B);A=AugmentationSet();A.add_augmentation(C);A.add_augmentation(D);assert str(A)==B;assert repr(A)==B
```

==========
Prog. Lang. Code File: test_vector_db_augmentations.py
Size: 4.12 kilobytes
==========

test_vector_db_augmentations.py
```py
_F='Third document with different content'
_E='Second test document'
_D='First test document'
_C='vector_db_api'
_B='test_augmentation'
_A='index'
import os,tempfile,uuid,pytest
from litemind import VECDB_IMPLEMENTATIONS
from litemind.agent.augmentations.augmentation_base import AugmentationBase
from litemind.agent.augmentations.information.information import Information
from litemind.agent.augmentations.vector_db.tests.utils.hash_embeddings import simple_hash_embeddings
from litemind.media.types.media_text import Text
def _get_temp_folder():return os.path.join(tempfile.gettempdir(),'augmentation_test_'+str(uuid.uuid4()))
@pytest.mark.parametrize(_C,VECDB_IMPLEMENTATIONS)
def test_augmentation_init(vector_db_api):C='Test description';B=vector_db_api;A=B(name=_B,description=C,location=_get_temp_folder());assert isinstance(A,AugmentationBase);assert A.name==_B;assert A.description==C;A=B(name='test_augmentation2',location=_get_temp_folder());assert A.description=='test_augmentation2 Augmentation'
@pytest.mark.parametrize(_C,VECDB_IMPLEMENTATIONS)
def test_get_relevant_documents(vector_db_api):B=vector_db_api(location=_get_temp_folder(),embedding_function=lambda texts:simple_hash_embeddings(texts,dim=128));C=[Information(Text(_D),metadata={_A:1}),Information(Text(_E),metadata={_A:2}),Information(Text(_F),metadata={_A:3})];B.add_informations(C);A=B.get_relevant_informations('test document',k=2);assert isinstance(A,list);assert 1<=len(A)<=2;assert all(isinstance(A,Information)for A in A);D=Information(Text('test document query'));A=B.get_relevant_informations(D,k=2);assert isinstance(A,list);assert len(A)<=2;assert all(isinstance(A,Information)for A in A)
@pytest.mark.parametrize(_C,VECDB_IMPLEMENTATIONS)
def test_get_relevant_documents_iterator(vector_db_api):B=vector_db_api(location=_get_temp_folder(),embedding_function=lambda texts:simple_hash_embeddings(texts,dim=128));D=[Information(Text(_D),metadata={_A:1}),Information(Text(_E),metadata={_A:2}),Information(Text(_F),metadata={_A:3})];B.add_informations(D);A=B.get_relevant_informations_iterator('test query',k=2);assert hasattr(A,'__iter__');assert hasattr(A,'__next__');C=list(A);assert len(C)<=2;assert all(isinstance(A,Information)for A in C)
@pytest.mark.parametrize(_C,VECDB_IMPLEMENTATIONS)
def test_string_representations(vector_db_api):B=vector_db_api;A=B(name=_B,location=_get_temp_folder());assert str(A)==_B;assert _B in repr(A);assert B.__name__ in repr(A)
```




==========
Prog. Lang. Code File: base_vector_db.py
Size: 3.36 kilobytes
==========

base_vector_db.py
```py
from abc import ABC,abstractmethod
from typing import List,Optional,Union
from pydantic import BaseModel
from litemind.agent.augmentations.information.information import Information
from litemind.media.media_base import MediaBase
class BaseVectorDatabase(ABC):
	@abstractmethod
	def add_informations(self,informations,embeddings=None):0
	@abstractmethod
	def get_information(self,information_id):0
	@abstractmethod
	def similarity_search(self,query,k=5,threshold=.0):0
	@abstractmethod
	def delete(self,ids):0
	@abstractmethod
	def clear(self):0
	@abstractmethod
	def save(self):0
	@abstractmethod
	def close(self):0
```

==========
Prog. Lang. Code File: default_vector_db.py
Size: 10.11 kilobytes
==========

default_vector_db.py
```py
_A=None
import json
from typing import Callable,List,Optional,Sequence
from pydantic import BaseModel
from litemind.agent.augmentations.augmentation_default import AugmentationDefault
from litemind.agent.augmentations.information.information import Information
from litemind.agent.augmentations.vector_db.base_vector_db import BaseVectorDatabase
from litemind.apis.base_api import BaseApi
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_code import Code
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_json import Json
from litemind.media.types.media_object import Object
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
from litemind.utils.uri_utils import is_uri
class DefaultVectorDatabase(AugmentationDefault,BaseVectorDatabase):
	def __init__(A,name=_A,description=_A,embedding_function=_A,embedding_length=_A,api=_A):
		D=api;C=embedding_length;B=name;super().__init__(name=B,description=description)
		if B is _A:B=A.__class__.__name__
		A.name=B
		if D is _A:
			try:from litemind import CombinedApi as E;D=E()
			except ValueError:raise ValueError("No API provided and litemind's default API is not available. Please provide an API.")
		A.api=D;A.embedding_function=embedding_function or A._default_embedding_function
		if C is _A:F=Information(Text('sample text'));G=A.embedding_function([F])[0];C=len(G)
		A.embedding_length=C
	def _default_embedding_function(A,informations):
		B=informations
		if not hasattr(A,'embedding_length'):A.embedding_length=768
		D={A.type for A in B}
		if len(D)>1:
			C=[]
			for E in B:F=A._raw_embedding_function([E]);C.append(F[0])
		else:C=A._raw_embedding_function(B)
		return C
	def _raw_embedding_function(B,informations):
		A=informations
		if not A:raise ValueError('No informations provided for embedding.')
		if all(A.embedding is not _A for A in A):C=[A.embedding for A in A]
		elif all(A.has_type(Text)for A in A):E=[A.content for A in A];C=B.api.embed_texts(texts=E,dimensions=B.embedding_length)
		elif all(A.has_type(Code)for A in A):F=[A.content for A in A];C=B.api.embed_texts(texts=F,dimensions=B.embedding_length)
		elif all(A.has_type(Json)for A in A):D=[json.dumps(A.content)if isinstance(A.content,dict)else A.content for A in A];C=B.api.embed_texts(texts=D,dimensions=B.embedding_length)
		elif all(A.has_type(Object)for A in A):
			if any(not isinstance(A.content,BaseModel)for A in A):raise ValueError('Information content must be a Pydantic BaseModel for object type Object.')
			D=[json.dumps(A.content.model_dump(mode='json'))for A in A];C=B.api.embed_texts(texts=D,dimensions=B.embedding_length)
		elif all(A.has_type(Image)for A in A):G=[A.content for A in A];C=B.api.embed_images(image_uris=G,dimensions=B.embedding_length)
		elif all(A.has_type(Audio)for A in A):H=[A.content for A in A];C=B.api.embed_audios(audio_uris=H,dimensions=B.embedding_length)
		elif all(A.has_type(Video)for A in A):I=[A.content for A in A];C=B.api.embed_videos(video_uris=I,dimensions=B.embedding_length)
		elif all(A.has_type(Document)for A in A):J=[A.content for A in A];C=B.api.embed_documents(document_uris=J,dimensions=B.embedding_length)
		elif all(A.has_type(Table)for A in A):
			if any(not is_uri(A.content)for A in A):raise ValueError('All informations content must be a URI for table type.')
			K=[A.media.to_markdown()for A in A];C=B.api.embed_texts(texts=K,dimensions=B.embedding_length)
		else:raise ValueError(f"Unknown document type or document: {A[0].type}.")
		return C
	def __del__(A):
		try:A.close()
		except:pass
```

==========
Prog. Lang. Code File: in_memory_vector_db.py
Size: 15.61 kilobytes
==========

in_memory_vector_db.py
```py
_I='embedding_length'
_H='index_to_id'
_G='id_to_index'
_F='informations'
_E='vectors.npy'
_D='metadata.pkl'
_C=False
_B=True
_A=None
import copy,os,pickle,uuid
from typing import Callable,Dict,List,Optional,Union
import numpy as np
from arbol import aprint
from pydantic import BaseModel
from scipy.spatial import cKDTree
from litemind.agent.augmentations.information.information import Information
from litemind.agent.augmentations.information.information_base import InformationBase
from litemind.agent.augmentations.vector_db.default_vector_db import DefaultVectorDatabase
from litemind.apis.base_api import BaseApi
from litemind.media.media_base import MediaBase
class InMemoryVectorDatabase(DefaultVectorDatabase):
	def __init__(A,name='InMemoryVectorDB',description=_A,embedding_function=_A,embedding_length=_A,location=_A,api=_A):
		C=api;B=location;super().__init__(name=name,description=description,embedding_function=embedding_function,embedding_length=embedding_length,api=C)
		if C is _A:from litemind import CombinedApi as D;A.api=D()
		else:A.api=C
		A.location=B;A.informations={};A.embedding_vectors=_A;A.kdtree=_A;A.id_to_index={};A.index_to_id={};A.rebuild_needed=_C
		if B and A._database_exists(B):A._load();return
	def _database_exists(D,location):A=location;B=os.path.join(A,_D);C=os.path.join(A,_E);return os.path.exists(B)and os.path.exists(C)
	def _load(A):
		try:
			if not os.path.exists(A.location):return
			C=os.path.join(A.location,_D)
			if not os.path.exists(C):return
			with open(C,'rb')as E:B=pickle.load(E)
			A.informations=B[_F];A.id_to_index=B[_G];A.index_to_id=B[_H];A.embedding_length=B[_I];D=os.path.join(A.location,_E)
			if os.path.exists(D):A.embedding_vectors=np.load(D)
			A.rebuild_needed=_B;A._rebuild_kdtree(force=_B);aprint(f"Loaded vector database from {A.location} with {len(A.informations)} informations")
		except Exception as F:aprint(f"Error loading database: {F}");A.informations={};A.embedding_vectors=_A;A.id_to_index={};A.index_to_id={};A.rebuild_needed=_C
	def _rebuild_kdtree(A,force=_C):
		if not force and not A.rebuild_needed:return
		if A.embedding_vectors is _A or len(A.informations)==0:A.kdtree=_A;A.id_to_index={};A.index_to_id={};return
		A.kdtree=cKDTree(A.embedding_vectors);A.rebuild_needed=_C
	def add_informations(A,informations,embeddings=_A):
		D=informations;B=embeddings
		if not D:return[]
		G=[]
		for F in D:
			if F.id is _A:F.set_id(str(uuid.uuid4()))
			G.append(F.id)
		if B is _A:
			aprint(f"Computing embeddings for {len(D)} informations");B=A.embedding_function(D)
			for(C,F)in enumerate(D):F.embedding=B[C]
		B=np.array(B);B=B/np.maximum(np.linalg.norm(B,axis=1,keepdims=_B),1e-10)
		if A.embedding_vectors is _A:
			A.embedding_vectors=B
			for(C,E)in enumerate(G):A.informations[E]=D[C];A.id_to_index[E]=C;A.index_to_id[C]=E
		else:
			H=len(A.informations)
			for(C,E)in enumerate(G):A.informations[E]=D[C];A.id_to_index[E]=H+C;A.index_to_id[H+C]=E
			A.embedding_vectors=np.vstack([A.embedding_vectors,B])
		A.rebuild_needed=_B;return G
	def get_information(A,information_id):return A.informations.get(information_id)
	def delete(A,ids):
		if not ids:return
		C=[]
		for B in ids:
			if B in A.id_to_index:C.append(A.id_to_index[B]);del A.informations[B];del A.id_to_index[B]
		if C and A.embedding_vectors is not _A:
			D=np.ones(len(A.embedding_vectors),dtype=bool);D[C]=_C;A.embedding_vectors=A.embedding_vectors[D];A.index_to_id={};A.id_to_index={}
			for(E,B)in enumerate(A.informations.keys()):A.id_to_index[B]=E;A.index_to_id[E]=B
			A.rebuild_needed=_B
	def clear(A):A.informations.clear();A.embedding_vectors=_A;A.kdtree=_A;A.id_to_index.clear();A.index_to_id.clear();A.rebuild_needed=_C
	def similarity_search(A,query,k=5,threshold=.0):
		B=query
		if not A.informations:return[]
		if k<=0:return[]
		A._rebuild_kdtree()
		if isinstance(B,list)and isinstance(B[0],float):C=B
		else:B=A._normalize_query(B);B=Information(B);C=list(A.embedding_function([B])[0])
		C=np.array(C);G=np.linalg.norm(C)
		if G>0:C=C/G
		k=min(k,len(A.informations))
		if A.kdtree is _A:return[]
		D,E=A.kdtree.query(C,k=k);F=[]
		if k==1:E=[int(E)];D=[float(D)]
		else:E=[int(A)for A in E];D=[float(A)for A in D]
		for(H,K)in enumerate(E):
			L=A.index_to_id[K];I=copy.copy(A.informations[L]);J=1.-D[H]*D[H]/2.;I.score=J
			if J>=threshold:F.append(I)
		F.sort(key=lambda x:x.score,reverse=_B);return F
	def get_relevant_informations(B,query,k=5,threshold=.0):A=query;A=B._normalize_query(A);return B.similarity_search(A,k,threshold)
	def save(A):
		if not A.location:aprint("No location provided, can't save the database");return
		try:
			os.makedirs(A.location,exist_ok=_B);B={_F:A.informations,_G:A.id_to_index,_H:A.index_to_id,_I:A.embedding_length}
			with open(os.path.join(A.location,_D),'wb')as C:pickle.dump(B,C,protocol=pickle.HIGHEST_PROTOCOL)
			if A.embedding_vectors is not _A:np.save(os.path.join(A.location,_E),A.embedding_vectors)
			aprint(f"Saved vector database to {A.location} with {len(A.informations)} informations")
		except Exception as D:aprint(f"Error saving database: {D}");import traceback as E;E.print_stack()
	def close(A):A.save()
```

==========
Prog. Lang. Code File: qdrant_vector_db.py
Size: 10.74 kilobytes
==========

qdrant_vector_db.py
```py
_B='info'
_A=None
import os,uuid
from typing import Callable,List,Optional,Union
import numpy as np
from arbol import aprint
from pydantic import BaseModel
from litemind.agent.augmentations.information.information import Information
from litemind.agent.augmentations.vector_db.default_vector_db import DefaultVectorDatabase
from litemind.apis.base_api import BaseApi
from litemind.media.media_base import MediaBase
class QdrantVectorDatabase(DefaultVectorDatabase):
	def __init__(A,name='QdrantVectorDB',description=_A,collection_name='informations',embedding_function=_A,embedding_length=_A,url=_A,location=_A,qdrant_api_key=_A,api=_A):
		C=location;B=collection_name;super().__init__(name=name,description=description,embedding_function=embedding_function,embedding_length=embedding_length,api=api);A.collection_name=B
		try:
			from qdrant_client import QdrantClient as D;from qdrant_client.http import models as E
			if url:A.client=D(url=url,api_key=qdrant_api_key)
			else:
				if C is _A:F=os.path.expanduser('~');C=os.path.join(F,'.qdrant_data');os.makedirs(C,exist_ok=True)
				A.client=D(path=C)
			G=A.client.get_collections().collections;H=[A.name for A in G]
			if B not in H:aprint(f"Creating new Qdrant collection: {B}");A.client.create_collection(collection_name=B,vectors_config=E.VectorParams(size=A.embedding_length,distance=E.Distance.COSINE))
		except ImportError:raise ImportError("Qdrant not installed. Please install it with 'pip install qdrant-client'.")
	def add_informations(C,informations,embeddings=_A):
		D=embeddings;A=informations;from qdrant_client.http import models as F
		for B in A:
			if B.id is _A:B.set_id(str(uuid.uuid4()))
		if D is _A:aprint(f"Computing embeddings for {len(A)} informations");D=C.embedding_function(A)
		E=[]
		for(B,G)in zip(A,D):H={_B:B.to_base64()};E.append(F.PointStruct(id=B.id,vector=G,payload=H))
		C.client.upsert(collection_name=C.collection_name,points=E);return[A.id for A in A]
	def get_information(A,information_id):
		B=A.client.retrieve(collection_name=A.collection_name,ids=[information_id])
		if not B:return
		C=B[0];return Information.from_base64(C.payload[_B])
	def similarity_search(C,query,k=5,threshold=.0):
		A=query
		if k<=0:return[]
		if isinstance(A,list)and isinstance(A[0],float):B=A
		else:A=C._normalize_query(A);A=Information(A);B=list(C.embedding_function([A])[0])
		B=np.array(B);E=np.linalg.norm(B)
		if E>0:B=B/E
		H=C.client.query_points(collection_name=C.collection_name,query=B,limit=k);F=[]
		for G in H.points:
			D=Information.from_base64(G.payload[_B]);D.score=G.score
			if D.score>threshold:F.append(D)
		return F
	def delete(A,ids):A.client.delete(collection_name=A.collection_name,points_selector=ids)
	def clear(A):
		try:A.client.delete_collection(collection_name=A.collection_name);from qdrant_client.http import models as B;A.client.create_collection(collection_name=A.collection_name,vectors_config=B.VectorParams(size=A.embedding_length,distance=B.Distance.COSINE))
		except Exception as C:aprint(f"Error clearing collection: {C}")
	def get_relevant_informations(B,query,k=5,threshold=.0):A=query;A=B._normalize_query(A);return B.similarity_search(A,k,threshold)
	def save(A):0
	def close(A):
		if hasattr(A,'client')and A.client is not _A:A.client.close()
```




==========
Prog. Lang. Code File: test_in_memory_vector_db.py
Size: 2.57 kilobytes
==========

test_in_memory_vector_db.py
```py
import shutil,tempfile,pytest
from litemind.agent.augmentations.information.information import Information
from litemind.agent.augmentations.vector_db.in_memory_vector_db import InMemoryVectorDatabase
from litemind.media.types.media_text import Text
class TestInMemoryVectorDatabase:
	@pytest.fixture
	def test_infos(self):return[Information(Text('This is the first test information'),id='doc1'),Information(Text('Another information for testing'),id='doc2'),Information(Text('Third information with different content'),id='doc3')]
	@pytest.fixture
	def db_dir(self):A=tempfile.mkdtemp();yield A;shutil.rmtree(A)
	def test_save_load(O,test_infos,db_dir):
		L='info4';K='test information';F='TestDB';C=db_dir;A=test_infos;D=InMemoryVectorDatabase(name=F,location=C);D.add_informations(A);E=D.similarity_search(K,k=2);assert len(E)==2;assert E[0].id in[A.id for A in A];D.save();B=InMemoryVectorDatabase(name=F,location=C);assert len(B.informations)==len(A)
		for M in[A.id for A in A]:assert M in B.informations
		for G in A:H=B.get_information(G.id);assert H is not None;assert H.media==G.media
		I=B.similarity_search(K,k=2);assert len(I)==2;assert set(A.id for A in E)==set(A.id for A in I);N=Information(Text('Fourth information added after loading'),id=L);B.add_informations([N]);B.save();J=InMemoryVectorDatabase(name=F,location=C);assert len(J.informations)==len(A)+1;assert L in J.informations
```

==========
Prog. Lang. Code File: test_multimodal_vector_dbs.py
Size: 12.60 kilobytes
==========

test_multimodal_vector_dbs.py
```py
_K='Harvard sentences'
_J='speech'
_I='harvard.wav'
_H='python.png'
_G='Python programming'
_F='Python is a programming language'
_E='language'
_D='subject'
_C='python'
_B='vector_db_api'
_A='index'
import hashlib,os,tempfile,uuid
from typing import List,Union
import pytest
from litemind import VECDB_IMPLEMENTATIONS
from litemind.agent.augmentations.information.information import Information
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_code import Code
from litemind.media.types.media_image import Image
from litemind.media.types.media_json import Json
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
from litemind.ressources.media_resources import MediaResources
def _get_temp_folder():return os.path.join(tempfile.gettempdir(),'multimodal_vecdb_test_'+str(uuid.uuid4()))
def multimodal_hash_embeddings(content,dim=128):
	B=content
	if not isinstance(B,list):B=[B]
	D=[]
	for A in B:
		if isinstance(A,str):
			if A.startswith('file://'):
				H=A[7:]
				try:
					with open(H,'rb')as I:J=I.read()
					C=hashlib.sha256(J)
				except:C=hashlib.sha256(A.encode())
			else:C=hashlib.sha256(A.encode())
		else:C=hashlib.sha256(A if isinstance(A,bytes)else str(A).encode())
		E=C.digest();F=[]
		for G in range(dim):K=E[G%len(E)]/255.;F.append((K*2-1)*(.8+.2*(G%7)/7))
		D.append(F)
	return D
class TestMultimodalVectorDB(MediaResources):
	@pytest.mark.parametrize(_B,VECDB_IMPLEMENTATIONS)
	def test_text_informations(self,vector_db_api):A=vector_db_api(location=_get_temp_folder(),embedding_function=lambda texts:multimodal_hash_embeddings(texts,dim=128));C=[Information(Text(_F),metadata={_A:1}),Information(Text('Machine learning algorithms process data'),metadata={_A:2}),Information(Text('Vector databases store and retrieve embeddings'),metadata={_A:3})];A.add_informations(C);B=A.get_relevant_informations(_G,k=2);assert len(B)>0;assert all(isinstance(A,Information)for A in B)
	@pytest.mark.parametrize(_B,VECDB_IMPLEMENTATIONS)
	def test_json_informations(self,vector_db_api):A=vector_db_api(location=_get_temp_folder(),embedding_function=lambda texts:multimodal_hash_embeddings(texts,dim=128));C=[Information(Json.from_string('{"name": "John", "age": 30, "city": "New York"}'),metadata={_A:1}),Information(Json.from_string('{"name": "Alice", "age": 25, "city": "Boston"}'),metadata={_A:2}),Information(Json.from_string('{"name": "Bob", "age": 35, "city": "Chicago"}'),metadata={_A:3})];A.add_informations(C);B=A.get_relevant_informations('{"city": "New York"}',k=1);assert len(B)>0;assert all(isinstance(A,Information)for A in B)
	@pytest.mark.parametrize(_B,VECDB_IMPLEMENTATIONS)
	def test_code_informations(self,vector_db_api):C='sql';A=vector_db_api(location=_get_temp_folder(),embedding_function=lambda texts:multimodal_hash_embeddings(texts,dim=128));D=[Information(Code("def hello():\n    print('Hello, world!')",lang=_C),metadata={_E:_C,_A:1}),Information(Code('function factorial(n) {\n    return n <= 1 ? 1 : n * factorial(n-1);\n}',lang=_C),metadata={_E:'javascript',_A:2}),Information(Code('SELECT * FROM users WHERE age > 18;',lang=C),metadata={_E:C,_A:3})];A.add_informations(D);B=A.get_relevant_informations('function factorial',k=2);assert len(B)>0;assert all(isinstance(A,Information)for A in B)
	@pytest.mark.parametrize(_B,VECDB_IMPLEMENTATIONS)
	def test_image_informations(self,vector_db_api):E='animal';A=self;C=vector_db_api(location=_get_temp_folder(),embedding_function=lambda content:multimodal_hash_embeddings(content,dim=128));F=A.get_local_test_image_uri(_H);D=A.get_local_test_image_uri('cat.jpg');G=A.get_local_test_image_uri('panda.jpg');H=[Information(Image(F),metadata={_D:'logo',_A:1}),Information(Image(D),metadata={_D:E,_A:2}),Information(Image(G),metadata={_D:E,_A:3})];C.add_informations(H);B=C.get_relevant_informations(D,k=1);assert len(B)>0;assert all(isinstance(A,Information)for A in B);assert any('cat'in A.content for A in B)
	@pytest.mark.parametrize(_B,VECDB_IMPLEMENTATIONS)
	def test_audio_informations(self,vector_db_api):B=vector_db_api(location=_get_temp_folder(),embedding_function=lambda content:multimodal_hash_embeddings(content,dim=128));C=self.get_local_test_audio_uri(_I);D=[Information(Audio(C),metadata={_J:_K,_A:1})];B.add_informations(D);A=B.get_relevant_informations(C,k=1);assert len(A)>0;assert all(isinstance(A,Information)for A in A);assert any('harvard'in A.content.lower()for A in A)
	@pytest.mark.parametrize(_B,VECDB_IMPLEMENTATIONS)
	def test_video_informations(self,vector_db_api):B=vector_db_api(location=_get_temp_folder(),embedding_function=lambda content:multimodal_hash_embeddings(content,dim=128));C=self.get_local_test_video_uri('flying.mp4');D=[Information(Video(C),metadata={_D:'flying object',_A:1})];B.add_informations(D);A=B.get_relevant_informations(C,k=1);assert len(A)>0;assert all(isinstance(A,Information)for A in A);assert any('flying'in A.content.lower()for A in A)
	@pytest.mark.parametrize(_B,VECDB_IMPLEMENTATIONS)
	def test_table_informations(self,vector_db_api):C='type';A=vector_db_api(location=_get_temp_folder(),embedding_function=lambda content:multimodal_hash_embeddings(content,dim=128));D='name,age,city\nJohn,30,New York\nAlice,25,Boston\nBob,35,Chicago';E='product,price,quantity\nApple,1.20,100\nBanana,0.50,150\nOrange,0.80,75';F=[Information(Table.from_csv_string(D),metadata={C:'people',_A:1}),Information(Table.from_csv_string(E),metadata={C:'products',_A:2})];A.add_informations(F);B=A.get_relevant_informations('name,age',k=1);assert len(B)>0;assert all(isinstance(A,Information)for A in B)
	@pytest.mark.parametrize(_B,VECDB_IMPLEMENTATIONS)
	def test_document_informations(self,vector_db_api):A=vector_db_api(location=_get_temp_folder(),embedding_function=lambda content:multimodal_hash_embeddings(content,dim=128));B=self.get_local_test_image_uri(_H);C=self.get_local_test_audio_uri(_I);D=[Information(Text(_F),metadata={_A:1}),Information(Image(B),metadata={_D:'logo',_A:2}),Information(Audio(C),metadata={_J:_K,_A:3}),Information(Code('def fibonacci(n):\n    return n if n <= 1 else fibonacci(n-1) + fibonacci(n-2)',lang=_C),metadata={_E:_C,_A:4})];A.add_informations(D);E=A.get_relevant_informations(_G,k=2);assert len(E)>0;F=A.get_relevant_informations(B,k=1);assert len(F)>0
```

==========
Prog. Lang. Code File: test_multimodal_vector_dbs_real_embed.py
Size: 13.37 kilobytes
==========

test_multimodal_vector_dbs_real_embed.py
```py
_S='people'
_R='flying object'
_Q='Harvard sentences'
_P='speech'
_O='name,age,city\nJohn,30,New York\nAlice,25,Boston\nBob,35,Chicago'
_N='flying.mp4'
_M='cat.jpg'
_L='Python programming'
_K='name'
_J='animal'
_I='harvard.wav'
_H='python.png'
_G='language'
_F='test'
_E='subject'
_D='vector_db_api'
_C='python'
_B='type'
_A='index'
import os,tempfile,uuid,pytest
from litemind import VECDB_IMPLEMENTATIONS
from litemind.agent.augmentations.information.information import Information
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_code import Code
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_json import Json
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
from litemind.ressources.media_resources import MediaResources
def _get_temp_folder():return os.path.join(tempfile.gettempdir(),'augmentation_test_'+str(uuid.uuid4()))
class TestMultimodalRelevantInformations(MediaResources):
	@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
	def test_get_relevant_informations_multimodal_basic(self,vector_db_api):F='image';B=self;A=vector_db_api(location=_get_temp_folder());C=B.get_local_test_image_uri(_H);G=B.get_local_test_image_uri(_M);D=B.get_local_test_audio_uri(_I);H=B.get_local_test_video_uri(_N);I=Table.from_csv_string(_O);J=B.get_local_test_document_uri('intracktive_preprint.pdf');K=Document(J);L=[Information(Text('Python programming guide'),metadata={_B:'text'}),Information(Code("def hello():\n    print('Hello world!')",lang=_C),metadata={_B:'code'}),Information(Image(C),metadata={_B:F,_E:'logo'}),Information(Image(G),metadata={_B:F,_E:_J}),Information(Audio(D),metadata={_P:_Q}),Information(Json({_K:_F,'value':42}),metadata={_B:'json'}),Information(Video(H),metadata={_E:_R}),Information(I,metadata={_B:_S}),Information(K,metadata={_B:'document'})];A.add_informations(L);E=A.get_relevant_informations(_L,k=3);assert len(E)>0;assert all(isinstance(A,Information)for A in E);M=Information(Text(_L));N=A.get_relevant_informations(M,k=3);assert len(N)>0;O=A.get_relevant_informations(Information(Code("print('Hello')",lang=_C)),k=3);assert len(O)>0;P=A.get_relevant_informations(C,k=3);assert len(P)>0;Q=A.get_relevant_informations(D,k=3);assert len(Q)>0
	@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
	def test_get_relevant_informations_code(self,vector_db_api):D='javascript';B='sql';A=vector_db_api(location=_get_temp_folder());E=[Information(Code("def hello():\n    print('Hello, world!')",lang=_C),metadata={_G:_C,_A:1}),Information(Code('function factorial(n) {\n    return n <= 1 ? 1 : n * factorial(n-1);\n}',lang=D),metadata={_G:D,_A:2}),Information(Code('SELECT * FROM users WHERE age > 18;',lang=B),metadata={_G:B,_A:3})];A.add_informations(E);C=A.get_relevant_informations('function factorial',k=2);assert len(C)>0;assert all(isinstance(A,Information)for A in C);F=A.get_relevant_informations(Information(Code('SELECT',lang=B)),k=2);assert len(F)>0
	@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
	def test_get_relevant_informations_images(self,vector_db_api):A=self;B=vector_db_api(location=_get_temp_folder());F=A.get_local_test_image_uri(_H);C=A.get_local_test_image_uri(_M);D=A.get_local_test_image_uri('panda.jpg');G=[Information(Image(F),metadata={_E:'logo',_A:1}),Information(Image(C),metadata={_E:_J,_A:2}),Information(Image(D),metadata={_E:_J,_A:3})];B.add_informations(G);E=B.get_relevant_informations(C,k=1);assert len(E)>0;assert all(isinstance(A,Information)for A in E);H=B.get_relevant_informations(Information(Image(D)),k=2);assert len(H)>0
	@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
	def test_get_relevant_informations_audio_video(self,vector_db_api):A=vector_db_api(location=_get_temp_folder());B=self.get_local_test_audio_uri(_I);C=self.get_local_test_video_uri(_N);D=[Information(Audio(B),metadata={_P:_Q,_A:1}),Information(Video(C),metadata={_E:_R,_A:1})];A.add_informations(D);E=A.get_relevant_informations(Information(Audio(B)),k=1);assert len(E)>0;F=A.get_relevant_informations(Information(Video(C)),k=1);assert len(F)>0
	@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
	def test_get_relevant_informations_table(self,vector_db_api):A=vector_db_api(location=_get_temp_folder());B=Table.from_csv_string(_O);C=Table.from_csv_string('product,price,quantity\nApple,1.20,100\nBanana,0.50,150\nOrange,0.80,75');D=[Information(B,metadata={_B:_S,_A:1}),Information(C,metadata={_B:'products',_A:2})];A.add_informations(D);E=Table.from_csv_string('name,age\nJohn,30');F=A.get_relevant_informations(Information(E),k=1);assert len(F)>0;G=A.get_relevant_informations('product prices',k=1);assert len(G)>0
	@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
	def test_get_relevant_informations_document(self,vector_db_api):A=vector_db_api(location=_get_temp_folder());B=self.get_local_test_document_uri('sample.pdf');C=[Information(Document(B),metadata={_B:'technical',_A:1})];A.add_informations(C);D=A.get_relevant_informations(Information(Document(B)),k=1);assert len(D)>0
	@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
	def test_get_relevant_informations_json(self,vector_db_api):D='John';C='weather';A='user';B=vector_db_api(location=_get_temp_folder());E=Json({C:{'temperature':25,'condition':'sunny'}});F=Json({A:{_K:D,'age':30,'roles':['admin','editor']}});G=[Information(E,metadata={_B:C,_A:1}),Information(F,metadata={_B:A,_A:2})];B.add_informations(G);H=Json({A:{_K:D}});I=B.get_relevant_informations(Information(H),k=1);assert len(I)>0
	@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
	def test_get_relevant_informations_mixed_media(self,vector_db_api):D='audio';A='topic';B=vector_db_api(location=_get_temp_folder());E=self.get_local_test_image_uri(_H);F=self.get_local_test_audio_uri(_I);G=[Information(Text('Python programming language guide'),metadata={A:_C}),Information(Code('import numpy as np',lang=_C),metadata={A:_C}),Information(Image(E),metadata={A:_C}),Information(Text('Harvard sentences used for audio testing'),metadata={A:D}),Information(Audio(F),metadata={A:D}),Information(Json({_G:_C,'version':'3.9'}),metadata={A:_C})];B.add_informations(G);C=B.get_relevant_informations(_L,k=5);assert len(C)>0;H={type(A.content).__name__ for A in C};assert len(H)>1
	@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
	def test_get_relevant_informations_edge_cases(self,vector_db_api):B='Duplicate content';A=vector_db_api(location=_get_temp_folder());C=A.get_relevant_informations('test query',k=5);assert len(C)==0;A.add_informations([Information(Text('Single test document'))]);D=A.get_relevant_informations(_F,k=1);assert len(D)==1;E=A.get_relevant_informations(_F,k=0);assert len(E)==0;F=A.get_relevant_informations(_F,k=5,threshold=.8);assert all(A.score>=.8 for A in F);A.clear();A.add_informations([Information(Text(B)),Information(Text(B))]);G=A.get_relevant_informations('Duplicate',k=3);assert 1<=len(G)<=2
```

==========
Prog. Lang. Code File: test_vector_dbs.py
Size: 10.34 kilobytes
==========

test_vector_dbs.py
```py
_H='index'
_G='Bob'
_F='Goodbye world'
_E='Alice'
_D='vector_db_api'
_C='Hello world'
_B='author'
_A=None
import random,string,time,uuid,pytest
from litemind import VECDB_IMPLEMENTATIONS
from litemind.agent.augmentations.information.information import Information
from litemind.agent.augmentations.vector_db.tests.utils.hash_embeddings import simple_hash_embeddings
from litemind.media.types.media_text import Text
def _get_temp_folder():import os,tempfile as A;return os.path.join(A.gettempdir(),'vector_db_test'+str(uuid.uuid4()))
@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
def test_add_documents(vector_db_api):D=vector_db_api(location=_get_temp_folder());B=Information(Text(_C),id=_A,metadata={_B:_E});C=Information(Text(_F),id=_A,metadata={_B:_G});A=D.add_informations([B,C]);assert len(A)==2;assert B.id in A;assert C.id in A
@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
def test_get_document(vector_db_api):B=vector_db_api(location=_get_temp_folder());C=Information(Text(_C),metadata={_B:_E});B.add_informations([C]);A=B.get_information(C.id);assert A is not _A;assert A.content==_C;assert A.metadata=={_B:_E}
@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
def test_similarity_search(vector_db_api):A=vector_db_api(location=_get_temp_folder());C=Information(Text(_C),id=_A,metadata={_B:_E});D=Information(Text(_F),id=_A,metadata={_B:_G});A.add_informations([C,D]);B=A.similarity_search('Hello',k=1);assert len(B)==1;assert B[0].content==_C
@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
def test_delete(vector_db_api):A=vector_db_api(location=_get_temp_folder());B=Information(Text(_C),metadata={_B:_E});C=Information(Text(_F),metadata={_B:_G});A.add_informations([B,C]);A.delete([B.id]);assert A.get_information(B.id)is _A;assert A.get_information(C.id)is not _A
@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
def test_clear(vector_db_api):A=vector_db_api(location=_get_temp_folder());B=Information(Text(_C),metadata={_B:_E});C=Information(Text(_F),metadata={_B:_G});A.add_informations([B,C]);A.clear();assert A.get_information(B.id)is _A;assert A.get_information(C.id)is _A
@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
def test_get_relevant_documents(vector_db_api):A=vector_db_api(location=_get_temp_folder());C=Information(Text(_C),id=_A,metadata={_B:_E});D=Information(Text(_F),id=_A,metadata={_B:_G});A.add_informations([C,D]);B=A.get_relevant_informations('Hello',k=1);assert len(B)==1;assert B[0].content==_C
@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
def test_complex_similarity_search(vector_db_api):E='value3';D='third content';A='key';C=vector_db_api(location=_get_temp_folder());F=[Information(Text('first content'),id=_A,metadata={A:'value1'}),Information(Text('second content'),id=_A,metadata={A:'value2'}),Information(Text(D),id=_A,metadata={A:E}),Information(Text('fourth content'),id=_A,metadata={A:'value4'})];C.add_informations(F);B=C.similarity_search('content #3',k=1);assert len(B)==1;assert B[0].content==D;assert B[0].metadata=={A:E}
@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
def test_vector_db_stress_test(vector_db_api):
	H=' ';A=5000;O=100;I=10;E=5
	def P(text):return simple_hash_embeddings(text,dim=256)
	B=vector_db_api(location=_get_temp_folder(),embedding_function=P);J=[];print(f"\nGenerating {A} random informations for {B.name}...")
	for C in range(A):
		F=H.join(random.choices(string.ascii_lowercase,k=O));K=f"unique_term_{C%100}"
		if C%3==0:F=F+H+K
		Q=Information(id=_A,media=Text(F),metadata={_H:C,'special_term':K if C%3==0 else _A});J.append(Q)
	G=time.time();R=B.add_informations(J);D=time.time()-G;assert len(R)==A;B.similarity_search('unique_term_0',k=E);L=0
	for C in range(I):
		if C%2==0:M=f"unique_term_{random.randint(0,99)}"
		else:M=H.join(random.choices(string.ascii_lowercase,k=5))
		G=time.time();S=B.similarity_search(M,k=E);T=time.time()-G;L+=T;assert len(S)<=E
	N=L/I;print(f"\nPerformance metrics for {B.name}:");print(f"- Documents: {A}");print(f"- Insertion time: {D:.4f} seconds ({A/D:.1f} docs/sec)");print(f"- Average query time: {N:.4f} seconds");print(f"Note: this benchmark does not include the computation of a complex embedding, we are using a simple hash embedding here.");return{'db_type':B.name,'num_documents':A,'insertion_time':D,'insertion_rate':A/D,'avg_query_time':N}
@pytest.mark.parametrize(_D,VECDB_IMPLEMENTATIONS)
def test_persistence(vector_db_api):H='test document';C=vector_db_api;D=_get_temp_folder();I=[Information(Text('This is the first test document'),id=_A,metadata={_H:1}),Information(Text('Another document for testing'),id=_A,metadata={_H:2}),Information(Text('Third document with different content'),id=_A,metadata={_H:3})];A=C(location=D);A.add_informations(I);E=A.similarity_search(H,k=2);assert len(E)==2;A.save();A.close();del A;B=C(location=D);F=B.similarity_search(H,k=2);assert len(F)==2;assert{A.content for A in E}=={A.content for A in F};J=Information(Text('Fourth document added after loading'),id=_A,metadata={_H:4});B.add_informations([J]);B.save();B.close();del B;K=C(location=D);G=K.similarity_search('fourth document',k=1);assert len(G)==1;assert'Fourth document'in G[0].content
```




==========
Prog. Lang. Code File: conversation.py
Size: 3.75 kilobytes
==========

conversation.py
```py
from typing import List,Sequence,Union
from litemind.agent.messages.message import Message
class Conversation:
	def __init__(A):A.system_messages=[];A.standard_messages=[]
	def get_all_messages(A):return A.system_messages+A.standard_messages
	def get_last_message(A):
		if A.standard_messages:return A.standard_messages[-1]
		else:return
	def clear_all(A):A.system_messages=[];A.clear_conversation()
	def clear_conversation(A):A.standard_messages=[]
	def append(B,message):
		A=message
		if A.role=='system':B.system_messages.append(A)
		else:B.standard_messages.append(A)
	def extend(A,messages):
		for B in messages:A.append(B)
	def __iadd__(A,other):
		B=other
		if isinstance(B,Message):A.append(B);return A
		elif isinstance(B,Conversation):A.system_messages.extend(B.system_messages);A.standard_messages.extend(B.standard_messages)
		else:raise ValueError('Can only add Conversation or Message objects')
		return A
	def __add__(B,other):C=other;A=Conversation();A.system_messages=B.system_messages+C.system_messages;A.standard_messages=B.standard_messages+C.standard_messages;return A
	def __getitem__(A,item):return A.get_all_messages()[item]
	def __len__(A):return len(A.get_all_messages())
	def __str__(A):return str(A.get_all_messages())
	def __repr__(A):return A.__str__()
```

==========
Prog. Lang. Code File: message.py
Size: 48.44 kilobytes
==========

message.py
```py
_E='file://'
_D='\n'
_C=True
_B=False
_A=None
import copy,fnmatch,os
from abc import ABC
from typing import Any,List,Optional,Sequence,Set,Type,Union
from arbol import aprint
from pydantic import BaseModel
from litemind.agent.messages.actions.tool_call import ToolCall
from litemind.agent.messages.actions.tool_use import ToolUse
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.media_base import MediaBase
from litemind.media.types.media_action import Action
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_code import Code
from litemind.media.types.media_document import Document
from litemind.media.types.media_file import File
from litemind.media.types.media_image import Image
from litemind.media.types.media_json import Json
from litemind.media.types.media_object import Object
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
from litemind.utils.extract_archive import extract_archive
from litemind.utils.file_types.file_extensions import OTHER_BINARY_EXTS
from litemind.utils.file_types.file_types import has_extension,is_archive_file,is_audio_file,is_document_file,is_executable_file,is_image_file,is_prog_code_file,is_script_file,is_text_file,is_video_file,is_web_file
from litemind.utils.folder_description import file_info_header,generate_tree_structure
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
from litemind.utils.uri_utils import is_uri
class Message(ABC):
	def __init__(A,text=_A,json=_A,obj=_A,image=_A,audio=_A,video=_A,document=_A,table=_A,role='user'):
		G=table;F=document;E=video;D=audio;C=image;B=role
		if not isinstance(B,str):raise ValueError(f"Role must be a string, not {type(B)}")
		if B not in['system','user','assistant','tool']:raise ValueError(f"Role must be 'system', 'user', or 'assistant', not {B}")
		A.role=B;A.blocks=[]
		if text:A.append_text(text)
		if json:A.append_json(json)
		if obj:A.append_object(obj)
		if C:A.append_image(C)
		if D:A.append_audio(D)
		if E:A.append_video(E)
		if F:A.append_document(F)
		if G:A.append_table(G)
	def copy(A):B=Message(role=A.role);B.blocks=A.blocks.copy();return B
	def __deepcopy__(A,memo):B=Message(role=A.role);B.blocks=[copy.deepcopy(A,memo)for A in A.blocks];return B
	def append_block(B,block):A=block;B.blocks.append(A);return A
	def append_blocks(B,blocks):
		A=blocks
		if isinstance(A,Message):A=A.blocks
		B.blocks.extend(A)
	def insert_block(A,block,block_before):B=block;C=A.blocks.index(block_before);A.blocks.insert(C+1,B);return B
	def insert_blocks(D,blocks,block_before):
		B=block_before;A=blocks
		for C in A:D.insert_block(C,B);B=C
		return A
	def insert_message(B,message,block_before):A=message;B.insert_blocks(A.blocks,block_before);return A
	def append_media(A,media,**B):return A.append_block(MessageBlock(media=media,**B))
	def append_text(A,text,**B):return A.append_block(MessageBlock(media=Text(text=text),**B))
	def append_thinking(C,thinking_text,**B):
		A=thinking_text
		if not isinstance(A,str):raise ValueError(f"Text must be a string, not {type(A)}")
		B['thinking']=_C;return C.append_block(MessageBlock(media=Text(text=A),**B))
	def append_json(A,json_str,source=_A,**B):return A.append_block(MessageBlock(media=Json(json_str),source=source,**B))
	def append_code(A,code,lang='python',source=_A,**B):return A.append_block(MessageBlock(media=Code(code=code,lang=lang),source=source,**B))
	def append_object(A,obj,source=_A,**B):return A.append_block(MessageBlock(media=Object(obj),**B))
	def append_image(A,image_uri,source=_A,**B):return A.append_block(MessageBlock(media=Image(uri=image_uri),source=source,**B))
	def append_audio(A,audio_uri,source=_A,**B):return A.append_block(MessageBlock(media=Audio(uri=audio_uri),source=source,**B))
	def append_video(A,video_uri,source=_A,**B):return A.append_block(MessageBlock(Video(uri=video_uri),source=source,**B))
	def append_document(A,document_uri,source=_A):return A.append_block(MessageBlock(Document(uri=document_uri),source=source))
	def append_table(E,table,source=_A):
		B=source;A=table;from numpy import ndarray as F;from pandas import DataFrame as D
		if isinstance(A,str)and is_uri(A):B=A;C=MessageBlock(Table(A),source=B)
		elif isinstance(A,F):A=D(A);C=MessageBlock(Table.from_dataframe(A),source=B)
		elif isinstance(A,D):C=MessageBlock(Table.from_dataframe(A),source=B)
		else:raise ValueError(f"Table must be a numpy array or a pandas DataFrame, not {type(A)}")
		return E.append_block(C)
	def append_file(C,file_uri,source=_A):
		A=file_uri
		if not A.startswith(_E):raise ValueError(f"File URI must be a local file URI that starts with 'file://', not '{A}'")
		B=A[7:]
		if not os.path.exists(B):raise ValueError(f"File '{B}' does not exist.")
		return C.append_block(MessageBlock(media=File(uri=A),source=source))
	def append_folder(B,folder,depth=_A,allowed_extensions=_A,excluded_files=_A,all_archive_files=_B,include_hidden_files=_B,append_tree_structure=_C,date_and_times=_B,file_sizes=_C):
		O=all_archive_files;L=include_hidden_files;K=allowed_extensions;I=excluded_files;G=depth;E=file_sizes;D=date_and_times;C=folder
		if G==0:B.append_text(f"Folder '{C}' is not traversed because depth is set to 0.");return
		C=os.path.abspath(C)
		if not os.path.exists(C):B.append_text(f"Folder '{C}' does not exist.");return
		if os.path.isdir(C)and not os.listdir(C):B.append_text(f"Folder '{C}' is empty.");return
		if os.path.isfile(C):B.append_text(f"File '{C}' is not a folder.");return
		if append_tree_structure:S=generate_tree_structure(C,allowed_extensions=K,excluded_files=I,include_hidden_files=L,depth=G);B.append_text(f"Directory structure:\n{S}")
		try:
			T=os.listdir(C);P=[];Q=[]
			for M in T:
				R=os.path.join(C,M)
				if os.path.isfile(R):P.append(M)
				elif os.path.isdir(R):Q.append(M)
			for H in sorted(P):
				if not L and(H.startswith('.')or H.startswith('__')):continue
				if I and any(fnmatch.fnmatch(H,A)for A in I):aprint(f"File '{H}' is excluded.");continue
				if K and not any(H.endswith(A)for A in K):aprint(f"File '{H}' is not allowed by the extensions filter.");continue
				A=os.path.join(C,H);F=_E+A
				if os.stat(A).st_size==0:U=file_info_header(A,'Empty',date_and_times=D,file_sizes=E);B.append_text(U+_D)
				elif is_prog_code_file(A):B.append_text(file_info_header(A,'Prog. Lang. Code',date_and_times=D,file_sizes=E));B.append_document(F,source=A)
				elif is_script_file(A):B.append_text(file_info_header(A,'Script',date_and_times=D,file_sizes=E));B.append_document(F,source=A)
				elif is_web_file(A):B.append_text(file_info_header(A,'Web',date_and_times=D,file_sizes=E));B.append_document(F,source=A)
				elif is_text_file(A):B.append_text(file_info_header(A,'Text',date_and_times=D,file_sizes=E));B.append_document(F,source=A)
				elif is_image_file(A):B.append_text(file_info_header(A,'Image',date_and_times=D,file_sizes=E));B.append_image(F,source=A)
				elif is_audio_file(A):B.append_text(file_info_header(A,'Audio',date_and_times=D,file_sizes=E));B.append_audio(F,source=A)
				elif is_video_file(A):B.append_text(file_info_header(A,'Video',date_and_times=D,file_sizes=E));B.append_video(F,source=A)
				elif is_document_file(A):B.append_text(file_info_header(A,'Document',date_and_times=D,file_sizes=E));B.append_document(F,source=A)
				elif is_archive_file(A):B.append_text(file_info_header(A,'Archive (depth={depth})',date_and_times=D,file_sizes=E));N=_A if O or not G else G-1;B.append_archive(F,N)
				elif is_executable_file(A):B.append_text(file_info_header(A,'Executable',date_and_times=D,file_sizes=E));B.append_file(F,source=A)
				elif has_extension(A,OTHER_BINARY_EXTS):B.append_text(file_info_header(A,'Binary',date_and_times=D,file_sizes=E));B.append_file(A,source=A)
				else:B.append_text(file_info_header(A,'Other',date_and_times=D,file_sizes=E));B.append_file(A,source=A)
			if G is _A or G>0:
				N=_A if G is _A else G-1
				for J in sorted(Q):
					if not L and(J.startswith('.')or J.startswith('__')):continue
					if I and any(fnmatch.fnmatch(J,A)for A in I):aprint(f"Folder '{J}' is excluded.");continue
					B.append_text(f"\n###### Sub-folder: {J}\n");B.append_folder(folder=os.path.join(C,J),depth=N,allowed_extensions=K,excluded_files=I,all_archive_files=O,include_hidden_files=L,append_tree_structure=_B)
		except Exception as V:import traceback as W;W.print_exc();B.append_text(f"Error accessing folder '{C}': {str(V)}")
	def append_archive(B,archive_uri,depth=-1):
		A=archive_uri
		try:D=uri_to_local_file_path(A);C=extract_archive(D);B.append_text(f"\n##### Contents of archive: {A} decompressed into folder: "+C+_D);B.append_folder(C,depth)
		except Exception as E:B.append_text(f"Error while attempting to extract files from archive '{A}': {str(E)}");B.append_file(A,source=A);return
	def append_tool_call(A,tool_name,arguments,id):B=MessageBlock(media=Action(ToolCall(tool_name,arguments,id)));return A.append_block(B)
	def append_tool_use(A,tool_name,arguments,result,id):B=MessageBlock(media=Action(ToolUse(tool_name,arguments,result,id)));return A.append_block(B)
	def extract_markdown_block(H,filters,remove_quotes=_C):
		G='```';C=filters
		if isinstance(C,str):C=[C]
		E=[]
		for F in H.blocks:
			if isinstance(F.media,Text)and any(A in F.get_content()for A in C):
				A=''+F.get_content()
				while G in A:
					B=A.find(G);D=A.find(G,B+3)
					if D==-1:break
					if remove_quotes:B=A.find(_D,B+3)+1;E.append(MessageBlock(media=Text(A[B:D])))
					else:E.append(MessageBlock(media=Text(A[B:D+3])))
					A=A[D+3:]
		return E
	def list_media_types(C):
		A=set()
		for B in C.blocks:
			if isinstance(B.media,MediaBase):A.add(type(B.media))
		return list(A)
	@staticmethod
	def list_media_types_in(messages):
		A=set();B:0
		for B in messages:
			C:0
			for C in B.list_media_types():A.add(C)
		return list(A)
	def convert_media(C,allowed_media_types=_A,media_converter=_A):
		B=allowed_media_types;A=media_converter
		if B is _A:B=[Text]
		if A is _A:from litemind.media.conversion.media_converter import MediaConverter as D;A=D();A.add_default_converters()
		E=A.convert([C],allowed_media_types=B);F=E[0];return F
	def compress_text(D,text_compressor=_A):
		B=text_compressor;C=Message(role=D.role)
		if B is _A:from litemind.utils.text_compressor import TextCompressor as F;B=F()
		for A in D.blocks:
			if isinstance(A.media,Text):
				E=B.compress(A.media.text)
				if len(E)>0:C.append_text(E,**A.attributes)
			else:C.append_block(A)
		return C
	def __getitem__(A,index):return A.blocks[index]
	def __len__(A):return len(A.blocks)
	def __contains__(A,item):
		for B in A.blocks:
			if item in str(B):return _C
		return _B
	def has(A,block_type):
		for B in A.blocks:
			if isinstance(B.media,block_type):return _C
		return _B
	def __str__(A):
		B=f"*{A.role}*:\n"
		for C in A.blocks:B+=str(C)+_D
		return B
	def lower(A):return A.__str__().lower()
	def to_plain_text(B):
		A=''
		for C in B.blocks:A+=str(C.media)+_D
		return A
	def to_markdown(B,media_converter=_A):
		A=media_converter
		if A is _A:from litemind.media.conversion.media_converter import MediaConverter as C;A=C();A.add_default_converters()
		from litemind.media.types.media_text import Text;D=A.convert(messages=[B],allowed_media_types=[Text]);E=D[0];F=E.to_plain_text();return F
	def report(H,as_string=_C):
		W='median_block_size';V='max_block_size';U='content_preview';R='min_block_size';Q='length';P='average_by_type';O='average_block_size';K='total_characters';J='total_blocks';G='longest_blocks';F='blocks_by_type';A={J:len(H.blocks),F:{},G:{},O:0,P:{},K:0,'role':H.role};D=[];L={}
		for S in H.blocks:
			B=type(S.media).__name__
			if B not in A[F]:A[F][B]=0;L[B]=[]
			A[F][B]+=1;I=str(S);E=len(I);D.append(E);L[B].append(E);A[K]+=E
			if B not in A[G]or E>A[G][B][Q]:M=I[:100]if len(I)>100 else I;M=M.replace(_D,'↩️');A[G][B]={Q:E,U:M+'...'}
		if A[J]>0:A[O]=A[K]/A[J]
		for(B,N)in L.items():
			if N:A[P][B]=sum(N)/len(N)
		if D:A[R]=min(D);A[V]=max(D);A[W]=sorted(D)[len(D)//2]
		if not as_string:return A
		C=f"Message Report (Role: {H.role})\n";C+=f"Total blocks: {A[J]}\n";C+=f"Total characters: {A[K]}\n\n";C+='Blocks by type:\n'
		for(B,X)in sorted(A[F].items()):C+=f"  {B}: {X} blocks\n"
		C+=f"\nAverage block size: {A[O]:.2f} characters\n";C+='\nAverage size by type:\n'
		for(B,Y)in sorted(A[P].items()):C+=f"  {B}: {Y:.2f} characters\n"
		C+='\nLongest blocks by type:\n'
		for(B,T)in sorted(A[G].items()):C+=f"  {B}: {T[Q]} characters\n";C+=f"    Preview: {T[U]}\n"
		if R in A:C+=f"\nMin block size: {A[R]} characters\n";C+=f"Max block size: {A[V]} characters\n";C+=f"Median block size: {A[W]} characters\n"
		return C
	def __repr__(A):return str(A)
	def __iter__(A):return iter(A.blocks)
	def __hash__(A):return hash((A.role,tuple(A.blocks)))
	def __eq__(B,other):
		A=other
		if not isinstance(A,Message):raise TypeError(f"Cannot compare {type(A)} with {type(B)}")
		if B.role!=A.role:return _B
		if len(B.blocks)!=len(A.blocks):return _B
		for(C,D)in zip(B.blocks,A.blocks):
			if C!=D:return _B
		return _C
	def __ne__(A,other):return not A==other
	def __add__(C,other):
		A=other;B=C.copy()
		if isinstance(A,Message):B.blocks+=A.blocks
		elif isinstance(A,MessageBlock):B.blocks.append(A)
		else:raise ValueError(f"Cannot concatenate message with {type(A)}. Only Message or MessageBlock are allowed.")
		return B
	def __iadd__(B,other):
		A=other
		if isinstance(A,Message):B.blocks+=A.blocks
		elif isinstance(A,MessageBlock):B.blocks.append(A)
		else:raise ValueError(f"Cannot concatenate message with {type(A)}. Only Message or MessageBlock are allowed.")
		return B
	def __radd__(C,other):
		A=other;B=A.copy()
		if isinstance(A,Message):B.blocks+=C.blocks
		elif isinstance(A,MessageBlock):B.blocks.append(C)
		else:raise ValueError(f"Cannot concatenate message with {type(A)}. Only Message or MessageBlock are allowed.")
		return B
```

==========
Prog. Lang. Code File: message_block.py
Size: 4.74 kilobytes
==========

message_block.py
```py
_A='thinking'
import copy
from typing import Any,Type
from litemind.media.media_base import MediaBase
from litemind.media.types.media_object import Object
from litemind.media.types.media_text import Text
class MessageBlock:
	def __init__(A,media,**B):A.media=media;A.attributes=B
	def get_content(A):return A.media.get_content()
	def has_type(A,block_type):return isinstance(A.media,block_type)
	def get_type(A):return type(A.media)
	def get_type_name(A):return A.get_type().__name__
	def has_attribute(A,attribute_key):return attribute_key in A.attributes
	def is_thinking(A):return A.has_attribute(_A)
	def is_redacted(A):return A.has_attribute('redacted')
	def copy(A):return MessageBlock(media=A.media,**A.attributes)
	def __deepcopy__(A,memo):return MessageBlock(media=copy.deepcopy(A.media,memo),**copy.deepcopy(A.attributes,memo))
	def contains(A,text):return text in str(A.get_content())
	def __str__(A):
		if isinstance(A.media,Text):return A.media.text
		elif _A in A.attributes:return f"<thinking>\n{A.get_content().strip()}\n<thinking/>\n"
		elif A.has_type(Object):return f"{type(A.get_content()).__name__}: {A.get_content()}"
		else:return f"{type(A.media).__name__}: {str(A.media)}"
	def __repr__(A):return str(A)
	def __len__(A):return len(A.media)
```




==========
Prog. Lang. Code File: action_base.py
Size: 423 bytes
==========

action_base.py
```py
from abc import abstractmethod
class ActionBase:
	@abstractmethod
	def __str__(self):0
	@abstractmethod
	def __repr__(self):0
	@abstractmethod
	def pretty_string(self):0
```

==========
Prog. Lang. Code File: tool_call.py
Size: 1.34 kilobytes
==========

tool_call.py
```py
from litemind.agent.messages.actions.action_base import ActionBase
class ToolCall(ActionBase):
	def __init__(A,tool_name,arguments=None,id=None):A.tool_name=tool_name;A.arguments=arguments;A.id=id
	def __str__(A):B=', '.join([f"{A}={B}"for(A,B)in A.arguments.items()]);return f"{A.tool_name}({B})"
	def __repr__(A):B=', '.join([f"{A}={B}"for(A,B)in A.arguments.items()]);return f"ToolUse(tool={A.tool_name}, arguments={B}, id={A.id})"
	def pretty_string(A):B=', '.join([f"{A}={B}"for(A,B)in A.arguments.items()]);return f"{A.tool_name}({B}) "
```

==========
Prog. Lang. Code File: tool_use.py
Size: 1.64 kilobytes
==========

tool_use.py
```py
from typing import Any
from litemind.agent.messages.actions.action_base import ActionBase
class ToolUse(ActionBase):
	def __init__(A,tool_name,arguments=None,result=None,id=None):A.tool_name=tool_name;A.arguments=arguments;A.id=id;A.result=result
	def __str__(A):B=', '.join([f"{A}={B}"for(A,B)in A.arguments.items()]);return f"{A.tool_name}({B})={A.result}"
	def __repr__(A):B=', '.join([f"{A}={B}"for(A,B)in A.arguments.items()]);return f"ToolUse(tool={A.tool_name}, arguments={B}, result={str(A.result)}, id={A.id})"
	def pretty_string(A):B=', '.join([f"{A}={B}"for(A,B)in A.arguments.items()]);return f"{A.tool_name}({B}) -> {str(A.result)} "
```




==========
Prog. Lang. Code File: test_conversation.py
Size: 768 bytes
==========

test_conversation.py
```py
from litemind.agent.messages.conversation import Conversation
from litemind.agent.messages.message import Message
def test_conversation():F='Who are you?';E='user';D='You are an omniscient all-knowing being called Ohmm';C='system';A=Conversation();G=Message(role=C,text=D);A.append(G);A+=Message(role=E,text=F);assert len(A)==2;B=A.get_all_messages();assert B[0].role==C;assert D in B[0];assert B[1].role==E;assert F in B[1];A.clear_all();assert len(A)==0
```

==========
Prog. Lang. Code File: test_message.py
Size: 14.44 kilobytes
==========

test_message.py
```py
_J='data'
_I='Can you describe what you see?'
_H='Second block'
_G='First block'
_F='https://example.com/document.pdf'
_E='https://example.com/video.mp4'
_D='https://example.com/audio.mp3'
_C='https://example.com/image.jpg'
_B='example'
_A='user'
import copy
from pprint import pprint
from pydantic import BaseModel
from litemind.agent.messages.message import Message
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
_remote_csv_file='https://www.sample-videos.com/csv/Sample-Spreadsheet-100-rows.csv'
class StructuredText(BaseModel):key:str;value:str
def test_message_deepcopy():A=Message(role=_A);C=MessageBlock(Text(_G));D=MessageBlock(Text(_H));A.append_block(C);A.append_block(D);B=copy.deepcopy(A);assert B.role==A.role;assert len(B)==len(A);assert B[0].media.text==A[0].media.text;assert B[1].media.text==A[1].media.text;assert B is not A;assert B.blocks is not A.blocks;assert B.blocks[0]is not A.blocks[0];assert B.blocks[1]is not A.blocks[1]
def test_insert_block():C='Inserted block';B=MessageBlock(Text(_G));D=MessageBlock(Text(_H));E=MessageBlock(Text(C));A=Message(role=_A);A.append_block(B);A.append_block(D);A.insert_block(E,block_before=B);assert A[0].media.get_content()==_G;assert A[1].media.get_content()==C;assert A[2].media.get_content()==_H
def test_insert_message():D='Third block';A=Message(role=_A);B=Message(role=_A);C=MessageBlock(Text(_G));E=MessageBlock(Text(_H));F=MessageBlock(Text(D));A.append_block(C);A.append_block(E);B.append_block(F);A.insert_message(B,block_before=C);assert A[0].media.get_content()==_G;assert A[1].media.get_content()==D;assert A[2].media.get_content()==_H
def test_message_text():E='Who are you?';D='You are an omniscient all-knowing being called Ohmm';C='system';A=Message(role=C);A.append_text(D);assert A.role==C;assert any(A.media.get_content()==D for A in A.blocks);B=Message(role=_A);B.append_text(E);assert B.role==_A;assert any(A.get_content()==E for A in B.blocks);assert'Who'in B;assert'omniscient'in A
def test_message_object():B=StructuredText(key=_B,value=_J);A=Message(role=_A);A.append_object(B);assert A.role==_A;assert any(A.get_content()==B for A in A.blocks)
def test_message_json():B=StructuredText(key=_B,value=_J);C=B.model_dump_json();A=Message(role=_A);A.append_json(C);assert A.role==_A;assert A.blocks[0].get_content()['key']==_B;assert A.blocks[0].get_content()['value']==_J
def test_message_image():A=Message(role=_A);A.append_text(_I);A.append_image(_C);assert A.role==_A;assert any(A.get_content()==_I for A in A.blocks);assert any(A.get_content()==_C and A.has_type(Image)for A in A.blocks);assert _B in A
def test_message_audio():B='Can you describe what you hear?';A=Message(role=_A);A.append_text(B);A.append_audio(_D);assert A.role==_A;assert any(A.get_content()==B for A in A.blocks);assert any(A.get_content()==_D and A.has_type(Audio)for A in A.blocks);assert _B in A
def test_message_video():B='Can you describe what you see in the video?';A=Message(role=_A);A.append_text(B);A.append_video(_E);assert A.role==_A;assert any(A.get_content()==B for A in A.blocks);assert any(A.get_content()==_E and A.has_type(Video)for A in A.blocks);assert _B in A
def test_message_document():B='Can you describe what you see in the document?';A=Message(role=_A);A.append_text(B);A.append_document(_F);assert A.role==_A;assert any(A.get_content()==B for A in A.blocks);assert any(B.get_content()==_F and B.has_type(Document)for B in A.blocks for B in A.blocks);assert _B in A
def test_message_table():B='Can you describe what you see in the table?';from pandas import DataFrame as C;D=C({'A':[1,2,3],'B':[4,5,6]});A=Message(role=_A);A.append_text(B);A.append_table(D);assert A.role==_A;assert any(A.get_content()==B for A in A.blocks);assert isinstance(A[1].media.to_dataframe(),C);assert'table'in A;A=Message(role=_A);A.append_text(B);A.append_table(_remote_csv_file);assert A.role==_A;assert any(A.get_content()==B for A in A.blocks);assert isinstance(A[1].media.to_dataframe(),C)
def test_message_folder():
	L='folder2';K='document.pdf';J='image.png';I='file.txt';G='Can you describe what you see in the folder?';F='folder1';E='w';import os as B,tempfile as M;D=M.mkdtemp()
	with open(B.path.join(D,I),E)as C:C.write('This is a random sentence.')
	import numpy as H;from PIL import Image as N;O=N.fromarray(H.random.randint(0,255,(100,100,3),dtype=H.uint8));O.save(B.path.join(D,J));import requests as P;Q='https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf';R=B.path.join(D,K)
	with open(R,'wb')as C:C.write(P.get(Q).content)
	B.makedirs(B.path.join(D,F,L))
	with open(B.path.join(D,F,'file1.txt'),E)as C:C.write('This is a random sentence 1.')
	with open(B.path.join(D,F,'file2.txt'),E)as C:C.write('This is a random sentence 2.')
	with open(B.path.join(D,F,L,'file3.txt'),E)as C:C.write('This is a random sentence 3.')
	with open(B.path.join(D,'.hidden_file.txt'),E)as C:C.write('This is a hidden file.')
	B.makedirs(B.path.join(D,'.hidden_folder'))
	with open(B.path.join(D,'empty_file.txt'),E)as C:0
	A=Message(role=_A);A.append_text(G);A.append_folder(D);pprint(A);assert A.role==_A;assert any(A.get_content()==G for A in A.blocks);assert any(A.get_content()==G for A in A.blocks);assert I in str(A);assert J in str(A);assert K in str(A);assert any(A.has_type(Document)for A in A.blocks);assert any(A.has_type(Image)for A in A.blocks);assert'Image File: image.png'in str(A);assert'Document File: document.pdf'in str(A);assert'Empty File: empty_file.txt'in str(A);assert'Text File: file.txt'in str(A);assert'Text File: file2.txt'in str(A);assert'Text File: file3.txt'in str(A)
def test_message_contains():A=Message(role=_A);A.append_text(_I);A.append_image(_C);A.append_audio(_D);A.append_video(_E);A.append_document(_F);A.append_table(_remote_csv_file);assert _B in A;assert _J not in A;assert'image'in A;assert'audio'in A;assert'video'in A;assert'document'in A;assert'Table'in A
def test_message_str():A=Message(role=_A);A.append_text(_I);A.append_image(_C);A.append_audio(_D);A.append_video(_E);A.append_document(_F);A.append_table(_remote_csv_file);assert _I in str(A);assert _C in str(A);assert _D in str(A);assert _E in str(A);assert _F in str(A);assert _remote_csv_file in str(A)
def test_extract_markdown_block():D='```markdown\n# Another Header\nMore content\n```';B=Message(role=_A);B.append_text('This is a text block:\n```markdown\n# Header\nSome content\n```');B.append_text('Another text block');B.append_text(D);C=['Header'];A=B.extract_markdown_block(C,remove_quotes=False);assert len(A)==2;assert A[0].get_content()=='```markdown\n# Header\nSome content\n```';assert A[1].get_content()==D;A=B.extract_markdown_block(C,remove_quotes=True);assert len(A)==2;assert A[0].get_content()=='# Header\nSome content\n';assert A[1].get_content()=='# Another Header\nMore content\n'
def test_list_present_media_types():A=Message(role=_A);A.append_text('This is a text block');A.append_image(_C);A.append_audio(_D);A.append_video(_E);A.append_document(_F);B=A.list_media_types();assert Text in B;assert Video in B;assert Audio in B;assert Image in B;assert Document in B
```

==========
Prog. Lang. Code File: test_message_append_folder.py
Size: 5.41 kilobytes
==========

test_message_append_folder.py
```py
_F='empty.txt'
_E='subfile.txt'
_D='.hidden'
_C='script.py'
_B='test.txt'
_A='user'
import os,tempfile
from pathlib import Path
import pytest
from litemind.agent.messages.message import Message
@pytest.fixture
def test_folder():
	with tempfile.TemporaryDirectory(delete=False)as D:A=Path(D)/'test_folder';A.mkdir();(A/_B).write_text('This is a test file');(A/_F).touch();(A/_C).write_text("print('Hello, World!')");(A/_D).write_text('Hidden content');B=A/'subfolder';B.mkdir();(B/_E).write_text('This is a file in a subfolder');C=A/'.hidden_folder';C.mkdir();(C/'hidden_file.txt').write_text('This is in a hidden folder');yield str(A)
def test_append_folder_basic(test_folder):A=Message(role=_A);A.append_folder(test_folder);assert len(A.blocks)>0;assert'Directory structure:'in str(A);assert _B in str(A);assert'/subfolder/subfile.txt'in str(A)
def test_append_folder_depth_limit(test_folder):A=Message(role=_A);A.append_folder(test_folder,depth=1);assert _B in str(A);assert _E not in str(A)
def test_append_folder_allowed_extensions(test_folder):A=Message(role=_A);A.append_folder(test_folder,allowed_extensions=['.py']);assert _C in str(A);assert _B not in str(A);assert _E not in str(A)
def test_append_folder_excluded_files(test_folder):A=Message(role=_A);A.append_folder(test_folder,excluded_files=[_B]);assert _B not in str(A);assert _C in str(A)
def test_append_folder_include_hidden_files(test_folder):A=test_folder;B=Message(role=_A);B.append_folder(A);assert _D not in str(B);C=Message(role=_A);C.append_folder(A,include_hidden_files=True);assert _D in str(C)
def test_append_folder_empty_file(test_folder):A=Message(role=_A);A.append_folder(test_folder);assert _F in str(A);assert'Empty'in str(A)
def test_append_folder_nonexistent_folder():A=Message(role=_A);B='/path/that/does/not/exist';A.append_folder(B);assert f"Folder '{B}' does not exist"in str(A)
def test_append_folder_empty_folder():
	with tempfile.TemporaryDirectory()as C:A=Path(C)/'empty_folder';A.mkdir();B=Message(role=_A);B.append_folder(str(A));assert f"Folder '{A}' is empty"in str(B)
def test_append_folder_file_path():
	with tempfile.NamedTemporaryFile(delete=False)as A:
		A.write(b'Test content');B=A.name;A.close()
		try:C=Message(role=_A);C.append_folder(B);assert f"File '{B}' is not a folder"in str(C)
		finally:os.unlink(B)
def test_append_folder_with_code_files(test_folder):A=Message(role=_A);A.append_folder(test_folder);assert'│   └── subfile.txt (29 bytes)'in str(A);assert'Empty File: empty.txt'in str(A);assert'Prog. Lang. Code File: script.py'in str(A)
```

==========
Prog. Lang. Code File: test_message_block.py
Size: 3.38 kilobytes
==========

test_message_block.py
```py
_A='example'
import copy
from pandas import DataFrame
from pydantic import BaseModel
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_object import Object
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
class ExampleModel(BaseModel):key:str;value:str
def test_message_block_deepcopy():
	A=MessageBlock(media=Text('Sample content'));B=copy.deepcopy(A);assert B.get_content()==A.get_content();assert B is not A
	if isinstance(A.get_content(),(list,dict)):assert B.get_content()is not A.get_content()
def test_message_block_text():B='This is a text block';A=MessageBlock(media=Text(B));assert A.get_content()==B;assert len(A)==len(B);assert A.contains('text');assert str(A)==B
def test_message_block_object():A=ExampleModel(key=_A,value='data');B=MessageBlock(media=Object(A));assert B.get_content()==A;assert len(B)==len(A.model_dump_json());assert B.contains(_A);assert str(B)==f"ExampleModel: {A}"
def test_message_block_image():B='https://example.com/image.jpg';A=MessageBlock(media=Image(B));assert A.media.uri==B;assert len(A)==len(B);assert A.contains(_A);assert str(A)=='Image: https://example.com/image.jpg'
def test_message_block_audio():B='https://example.com/audio.mp3';A=MessageBlock(Audio(B));assert A.media.uri==B;assert len(A)==len(B);assert A.contains(_A);assert str(A)=='Audio: https://example.com/audio.mp3'
def test_message_block_video():B='https://example.com/video.mp4';A=MessageBlock(Video(B));assert A.media.uri==B;assert len(A)==len(B);assert A.contains(_A);assert str(A)=='Video: https://example.com/video.mp4'
def test_message_block_document():B='https://example.com/document.pdf';A=MessageBlock(Document(B));assert A.media.uri==B;assert len(A)==len(B);assert A.contains(_A);assert str(A)=='Document: https://example.com/document.pdf'
def test_message_block_table():B=DataFrame({'A':[1,2,3],'B':[4,5,6]});A=MessageBlock(Table.from_dataframe(B));assert A.has_type(Table);assert A.media.to_dataframe().equals(B);assert len(A.get_content())>40;assert'Table:'in str(A)
```




==========
Prog. Lang. Code File: test_agent.py
Size: 5.15 kilobytes
==========

test_agent.py
```py
_B='api_class'
_A='You are an omniscient all-knowing being called Ohmm'
import pytest
from arbol import aprint
from pydantic import BaseModel
from litemind import API_IMPLEMENTATIONS
from litemind.agent.agent import Agent
from litemind.agent.messages.message import Message
from litemind.apis.model_features import ModelFeatures
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS)
def test_agent_with_just_text(api_class):
	E='Who are you?';D=api_class;C=D()
	if not C.has_model_support_for(features=ModelFeatures.TextGeneration):aprint(f"Skipping test for {D.__name__} as no text model is available.");return
	aprint(f"Default model: {C.get_best_model()}");A=Agent(api=C);A.append_system_message(_A);B=A(E);aprint(A.conversation);assert len(A.conversation)==3;assert A.conversation[0].role=='system';assert _A in A.conversation[0];assert A.conversation[1].role=='user';assert E in A.conversation[1];assert len(B)==1;B=B[-1];assert'Ohmm'in B
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS)
def test_agent_with_just_text(api_class):
	D=api_class;A=D()
	if not A.has_model_support_for(features=ModelFeatures.TextGeneration):aprint(f"Skipping test for {D.__name__} as no text model is available.");return
	aprint(f"Default model: {A.get_best_model()}")
	class F(BaseModel):temperature:float;condition:str;humidity:float
	E=Agent(api=A);E.append_system_message('You are a weather bot. Weather conditions must be: sunny, rainy, cloudy, snowy, or partly cloudy.');B=E("What is the weather like in Paris? (if you don't know, imagine what your favorite weather would be like)",response_format=F);print('Agent with JSON Response:',B);assert len(B)==1;C=B[-1][-1].get_content();assert C.humidity>30;assert C.temperature>10;assert C.condition.lower()in['sunny','rainy','cloudy','snowy','partly cloudy']
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS)
def test_agent_message_with_image(api_class):
	B=api_class;C=B()
	if not C.has_model_support_for(features=[ModelFeatures.TextGeneration,ModelFeatures.Image]):aprint(f"Skipping test for {B.__name__} as no text gen and image input model is available.");return
	D=C.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Image,ModelFeatures.Tools])
	if not D:aprint(f"Skipping test for {B.__name__} as no textgen + image + tools model is available.");return
	aprint(f"Image model: {D}");E=Agent(api=C,model_name=D);E.append_system_message(_A);F=Message(role='user',text='Can you describe what you see?');F.append_image('https://upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Einstein_1921_by_F_Schmutzer_-_restoration.jpg/456px-Einstein_1921_by_F_Schmutzer_-_restoration.jpg');A=E(F);assert len(E.conversation)==3;assert len(A)==1;A=A[-1];assert'sepia'in A or'photograph'in A or'chalkboard'in A
```

==========
Prog. Lang. Code File: test_agent_advanced.py
Size: 9.71 kilobytes
==========

test_agent_advanced.py
```py
_K='What is 21 multiplied by 2?'
_J='Add a certain number of days to a given date'
_I='What date is 15 days from today?'
_H='Returns the current date'
_G='api_class'
_F='%B'
_E='%m'
_D='No model supports text generation and tools. Skipping test.'
_C='0'
_B='%Y-%m-%d'
_A=None
from datetime import datetime,timedelta
import pytest
from litemind import API_IMPLEMENTATIONS
from litemind.agent.agent import Agent
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.model_features import ModelFeatures
API_IMPLEMENTATIONS_ADV_AGENT_TESTS=[A for A in API_IMPLEMENTATIONS if A.__name__!='OllamaApi']
def get_current_date():return datetime.now().strftime(_B)
def multiply_by_two(x):return x*2
def add_days_to_date(date_str,days):A=datetime.strptime(date_str,_B);B=A+timedelta(days=int(days));return B.strftime(_B)
def get_day_of_week(year,month,day):A=datetime(int(year),int(month),int(day));return A.strftime('%A')
@pytest.mark.parametrize(_G,API_IMPLEMENTATIONS)
def test_agent_single_tool(api_class):
	D=api_class();E=D.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Tools])
	if not E:pytest.skip(_D)
	F=ToolSet();F.add_function_tool(get_current_date,_H);H=Agent(api=D,model_name=E,toolset=F,temperature=.0);A=H('What date is today?');G=datetime.now().strftime(_B);I,C,B=G.split('-');C=datetime.strptime(C,_E).strftime(_F)
	if _C in B:B=B.replace(_C,'')
	B=int(B);A=str(A);assert A is not _A;assert G in A or I in A and C in A and(str(B-1)in A or str(B)in A or str(B+1)in A)
@pytest.mark.parametrize(_G,API_IMPLEMENTATIONS_ADV_AGENT_TESTS)
def test_agent_multiple_tools(api_class):
	E=api_class()
	if not E.has_model_support_for(features=[ModelFeatures.TextGeneration,ModelFeatures.Tools]):pytest.skip(_D)
	C=ToolSet();C.add_function_tool(get_current_date,_H);C.add_function_tool(multiply_by_two,'Multiply a number by 2');C.add_function_tool(add_days_to_date,_J);F=Agent(api=E,toolset=C,temperature=.0);A=F(_I);G=(datetime.now()+timedelta(days=15)).strftime(_B);H,D,B=G.split('-');D=datetime.strptime(D,_E).strftime(_F)
	if _C in B:B=B.replace(_C,'')
	B=int(B);A=str(A);assert A is not _A;assert G in A or H in A and D in A and(str(B-1)in A or str(B)in A or str(B+1)in A);A=F(_K);assert A is not _A;assert'42'in str(A)
@pytest.mark.parametrize(_G,API_IMPLEMENTATIONS_ADV_AGENT_TESTS)
def test_agent_chained_tools(api_class):
	F=api_class()
	if not F.has_model_support_for(features=[ModelFeatures.TextGeneration,ModelFeatures.Tools]):pytest.skip(_D)
	D=ToolSet();D.add_function_tool(get_current_date,_H);D.add_function_tool(add_days_to_date,_J);G=Agent(api=F,toolset=D,temperature=.0);A=G(_I);H=(datetime.now()+timedelta(days=15)).strftime(_B);I,E,B=H.split('-');E=datetime.strptime(E,_E).strftime(_F)
	if _C in B:B=B.replace(_C,'')
	B=int(B);A=str(A);assert A is not _A;assert H in A or I in A and E in A and(str(B-1)in A or str(B)in A or str(B+1)in A);D.add_function_tool(get_day_of_week,'Get the day of the week for a given date');A=G('Which day of the week is that?');assert A is not _A;C=str(A);assert'Monday'in C or'Tuesday'in C or'Wednesday'in C or'Thursday'in C or'Friday'in C or'Saturday'in C or'Sunday'in C
@pytest.mark.parametrize(_G,API_IMPLEMENTATIONS_ADV_AGENT_TESTS)
def test_agent_longer_dialog(api_class):
	G=api_class()
	if not G.has_model_support_for(features=[ModelFeatures.TextGeneration,ModelFeatures.Tools]):pytest.skip(_D)
	K=G.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Tools]);D=ToolSet();D.add_function_tool(get_current_date,'Returns the current date.');D.add_function_tool(add_days_to_date,'Add a certain number of days (positive or negative) to a given date.');D.add_function_tool(multiply_by_two,'Multiply a number by 2.');E=Agent(api=G,model_name=K,toolset=D,temperature=.0);B=E(_I);H=(datetime.now()+timedelta(days=15)).strftime(_B);I,F,A=H.split('-');F=datetime.strptime(F,_E).strftime(_F)
	if _C in A:A=A.replace(_C,'')
	A=int(A);B=str(B);assert B is not _A;assert H in B or I in B and F in B and(str(A-1)in B or str(A)in B or str(A+1)in B);J=E(_K);assert J is not _A;assert'42'in str(J);C=E('What is the double of the current date day number?');assert C is not _A;L=datetime.now().strftime(_B);I,F,A=L.split('-');C=str(C);assert str(int(A)*2)in C;C=E('Do you know what happened exactly 5 years ago (365*5 days ago)?');assert C is not _A;print(C)
```

==========
Prog. Lang. Code File: test_agent_augmentation.py
Size: 18.60 kilobytes
==========

test_agent_augmentation.py
```py
_F='category'
_E='subject'
_D='person'
_C='type'
_B='api_class'
_A='before_query'
import pytest
from pydantic import BaseModel
from litemind import API_IMPLEMENTATIONS,InMemoryVectorDatabase
from litemind.agent.agent import Agent
from litemind.agent.augmentations.information.information import Information
from litemind.apis.model_features import ModelFeatures
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_code import Code
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_json import Json
from litemind.media.types.media_object import Object
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
from litemind.ressources.media_resources import MediaResources
API_IMPLEMENTATIONS_AUG_AGENT_TESTS=[A for A in API_IMPLEMENTATIONS if A.__name__!='OllamaApi']
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_augmentation(api_class):
	P='concept';H=api_class;G='unitarity';F='Bolupskisty';E='physics';D='topic';C='test_augmentation';I=H()
	if not I.has_model_support_for(features=ModelFeatures.TextGeneration):pytest.skip(f"Skipping test for {H.__name__} as no text model is available.")
	A=Agent(api=I,augmentation_k=2,augmentation_context_position=_A);J=InMemoryVectorDatabase(name=C);Q=[Information(Text('Igor Bolupskisty was a German-born theoretical physicist who developed the theory of indelible unitarity.'),metadata={D:E,_D:F}),Information(Text('The theory of indelible unitarity revolutionized our understanding of space, time and photons.'),metadata={D:E,P:G}),Information(Text('Quantum unitarity is a fundamental theory in physics that describes nature at the nano-atomic scale as it pertains to Pink Hamsters.'),metadata={D:E,P:'quantum unitarity'})];J.add_informations(Q);A.add_augmentation(J);K=A.list_augmentations();assert len(K)==1;assert K[0].name==C;L=A("Tell me about Igor Bolupskisty's theory of indelible unitarity.");assert len(A.conversation)>=3;M=False
	for B in A.conversation.standard_messages:
		if B.role=='user'and'Additional context information'in B.to_plain_text():M=True;N=B.to_plain_text();assert F in N;assert G in N;break
	assert M,'Context message not found in conversation';assert len(L)>=1;O=L[-1].to_plain_text();assert F in O;assert G in O;A.remove_augmentation(C);assert len(A.list_augmentations())==0
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_text_document_augmentation(api_class):
	H='Pichilut';G='text_document_test';B=api_class;C=B()
	if not C.has_model_support_for(features=ModelFeatures.TextGeneration):pytest.skip(f"Skipping test for {B.__name__} as no text model is available.")
	A=Agent(api=C,augmentation_k=2,augmentation_context_position=_A);D=InMemoryVectorDatabase(name=G);I=Information(Text("The Gurov Tower is a wrought-iron lattice tower in Pichilut, Mars. It's named after engineer Gustavo Ffelei."),metadata={_C:'text',_E:'landmark','location':H});D.add_informations([I]);A.add_augmentation(D);E=A('Tell me about the Gurov Tower.');assert len(E)>=1;F=E[-1].to_plain_text();assert'Gurov'in F;assert H in F;A.remove_augmentation(G);assert len(A.list_augmentations())==0
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_image_document_augmentation(api_class):
	F='image_document_test';B=api_class;C=B()
	if not C.has_model_support_for(features=[ModelFeatures.TextGeneration,ModelFeatures.Image]):pytest.skip(f"Skipping test for {B.__name__} as no image understanding model is available.")
	A=Agent(api=C,augmentation_k=2,augmentation_context_position=_A);D=InMemoryVectorDatabase(name=F);G=Information(Image(MediaResources.get_local_test_image_uri('future.jpeg')));H=Information(Image(MediaResources.get_local_test_image_uri('beach.webp')));D.add_informations([G,H]);A.add_augmentation(D);E=A('Describe the futuristic image.');assert len(E)>=1;I=E[-1].to_plain_text();assert'image'in I.lower();A.remove_augmentation(F);assert len(A.list_augmentations())==0
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_video_document_augmentation(api_class):
	G='video_document_test';C=api_class;D=C()
	if not D.has_model_support_for(features=ModelFeatures.TextGeneration,media_types=[Video]):pytest.skip(f"Skipping test for {C.__name__} as no video understanding model is available.")
	A=Agent(api=D,augmentation_k=2,augmentation_context_position=_A);E=InMemoryVectorDatabase(name=G);H=Information(Video(MediaResources.get_local_test_video_uri('flying.mp4')));E.add_informations([H]);A.add_augmentation(E);F=A('What happens in the test video?');assert len(F)>=1;B=F[-1].to_plain_text().lower();assert'video'in B or'flying'in B or'hovering'in B or'clip'in B;A.remove_augmentation(G);assert len(A.list_augmentations())==0
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_pdf_document_augmentation(api_class):
	H='pdf_document_test';B=api_class;C=B()
	if not C.has_model_support_for(features=ModelFeatures.TextGeneration,media_types=[Document]):pytest.skip(f"Skipping test for {B.__name__} as no document analysis model is available.")
	A=Agent(api=C,augmentation_k=2,augmentation_context_position=_A);D=InMemoryVectorDatabase(name=H);E=Information(Document(MediaResources.get_local_test_document_uri('intracktive_preprint.pdf')));F=Information(Document(MediaResources.get_local_test_document_uri('low_discrepancy_sequence.pdf')));E.embedding=[.1,.2,.3];F.embedding=[.4,.5,.6];D.add_informations([E,F]);A.add_augmentation(D);G=A("What's in the test document?");assert len(G)>=1;I=G[-1].to_plain_text();assert'document'in I.lower();A.remove_augmentation(H);assert len(A.list_augmentations())==0
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_json_document_augmentation(api_class):
	G='json_document_test';B=api_class;C=B()
	if not C.has_model_support_for(features=[ModelFeatures.TextGeneration]):pytest.skip(f"Skipping test for {B.__name__} as no text model is available.")
	A=Agent(api=C,augmentation_k=2,augmentation_context_position=_A);D=InMemoryVectorDatabase(name=G);H=Information(Json.from_string('{"name": "John Doe", "age": 30, "city": "New York", "skills": ["Python", "Data Science"]}'),metadata={_C:'json',_E:_D,_F:'profile'});D.add_informations([H]);A.add_augmentation(D);E=A('What skills does John have?');assert len(E)>=1;F=E[-1].to_plain_text();assert'Python'in F or'Data Science'in F;A.remove_augmentation(G);assert len(A.list_augmentations())==0
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_code_document_augmentation(api_class):
	G='python';F='code_document_test';B=api_class;C=B()
	if not C.has_model_support_for(features=ModelFeatures.TextGeneration,media_types=[Code]):pytest.skip(f"Skipping test for {B.__name__} as no text model is available.")
	A=Agent(api=C,augmentation_k=2,augmentation_context_position=_A);D=InMemoryVectorDatabase(name=F);H=Information(Code('def function_abc(n):\n    a, b = 0, 1\n    for _ in range(n):\n        a, b = b, a + b\n    return a',lang=G),metadata={_C:'code','language':G,_F:'algorithm'});D.add_informations([H]);A.add_augmentation(D);E=A('Explain function abc.');assert len(E)>=1;I=E[-1].to_plain_text();assert'fibonacci'in I.lower();A.remove_augmentation(F);assert len(A.list_augmentations())==0
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_object_document_augmentation(api_class):
	I='Corolla';H='Toyota';G='object_document_test';B=api_class;C=B()
	if not C.has_model_support_for(features=[ModelFeatures.TextGeneration],media_types=[Object]):pytest.skip(f"Skipping test for {B.__name__} as no text model is available.")
	A=Agent(api=C,augmentation_k=2,augmentation_context_position=_A);D=InMemoryVectorDatabase(name=G)
	class J(BaseModel):make:str;model:str;year:int
	K=J(make=H,model=I,year=2020);L=Information(Object(K));D.add_informations([L]);A.add_augmentation(D);E=A('What car model do we have information about?');assert len(E)>=1;F=E[-1].to_plain_text();assert H in F or I in F;A.remove_augmentation(G);assert len(A.list_augmentations())==0
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_audio_document_augmentation(api_class):
	G='audio_document_test';C=api_class;D=C()
	if not D.has_model_support_for(features=[ModelFeatures.TextGeneration],media_types=[Audio]):pytest.skip(f"Skipping test for {C.__name__} as no audio understanding model is available.")
	A=Agent(api=D,augmentation_k=2,augmentation_context_position=_A);E=InMemoryVectorDatabase(name=G);H=Information(Audio(MediaResources.get_local_test_audio_uri('harvard.wav')));E.add_informations([H]);A.add_augmentation(E);F=A('What can you hear in the audio file?');assert len(F)>=1;B=F[-1].to_plain_text();print(B);assert'audio'in B.lower()or'recording'in B.lower()or'smell'in B.lower();A.remove_augmentation(G);assert len(A.list_augmentations())==0
@pytest.mark.parametrize(_B,API_IMPLEMENTATIONS_AUG_AGENT_TESTS)
def test_agent_with_table_document_augmentation(api_class):
	G='table_document_test';B=api_class;C=B()
	if not C.has_model_support_for(features=[ModelFeatures.TextGeneration],media_types=[Table]):pytest.skip(f"Skipping test for {B.__name__} as no text model is available.")
	A=Agent(api=C,augmentation_k=2,augmentation_context_position=_A);D=InMemoryVectorDatabase(name=G);H=Information(Table(MediaResources.get_local_test_table_uri('spreadsheet.csv')));D.add_informations([H]);A.add_augmentation(D);E=A('What data is in the table?');assert len(E)>=1;F=E[-1].to_plain_text();assert'table'in F.lower()or'data'in F.lower();A.remove_augmentation(G);assert len(A.list_augmentations())==0
```




==========
Prog. Lang. Code File: agent_tool.py
Size: 2.70 kilobytes
==========

agent_tool.py
```py
from typing import Any,Optional
from arbol import asection
from litemind.agent.agent import Agent
from litemind.agent.tools.base_tool import BaseTool
class AgentTool(BaseTool):
	def __init__(B,agent,description,has_memory=False):
		E='prompt';D='type';C=description;A=agent
		if not isinstance(A,Agent):raise ValueError('AgentTool must be initialized with an Agent object')
		if not A.name or not C:raise ValueError('AgentTool must be initialized with a name and description')
		super().__init__(A.name,C);B.agent=A;B.has_memory=has_memory;B.arguments_schema={D:'object','properties':{E:{D:'string'}},'required':[E],'additionalProperties':False}
	def execute(A,prompt):
		with asection(f"Executing tool agent '{A.name}'"):
			if not A.has_memory:A.agent.conversation.clear_conversation()
			B=A.agent(prompt);C=B[-1];D=C.to_plain_text();return D
	def pretty_string(A):
		if len(A.description)>80:B=A.description[:A.description.find('.',80)+1]+'[...]'
		else:B=A.description
		return f"{A.agent.name}(prompt: str) -> str  % {B}"
```

==========
Prog. Lang. Code File: base_tool.py
Size: 2.01 kilobytes
==========

base_tool.py
```py
from abc import ABC,abstractmethod
from typing import Any
class BaseTool(ABC):
	def __init__(A,name,description):
		A.name=''.join(name.strip().split());A.description=description.strip()
		if not A.description.endswith('.'):A.description+='.'
		A.arguments_schema={}
	def __repr__(A):return f"{A.name}(description={A.description})"
	def __str__(A):return A.__repr__()
	def is_builtin(A):from litemind.agent.tools.builtin_tools.builtin_tool import BuiltinTool as B;return isinstance(A,B)
	@abstractmethod
	def pretty_string(self):0
	@abstractmethod
	def execute(self,*A,**B):0
	def __call__(B,*A,**C):
		if A:raise ValueError('Positional arguments are not supported. Use keyword arguments instead.')
		return B.execute(*A,**C)
```

==========
Prog. Lang. Code File: function_tool.py
Size: 4.65 kilobytes
==========

function_tool.py
```py
_A='object'
import inspect
from typing import Any,Callable,Dict,Optional
from arbol import aprint,asection
from litemind.agent.tools.base_tool import BaseTool
from litemind.agent.tools.utils.inspect_function import extract_docstring
class FunctionTool(BaseTool):
	def __init__(A,func,description=None):
		E=description;D='***';C=func;super().__init__(name=C.__name__,description='');A.func=C
		if not E:
			B=extract_docstring(C)
			if D in B:A.description=B[B.find(D)+3:B.find(D,B.find(D)+1)]
			else:A.description=B
		else:A.description=E
		A.name=C.__name__;A.arguments_schema,A.arg_and_type=A._generate_arguments_schema()
	def _generate_arguments_schema(D):
		J='required';I='properties';H='type';E={};A={H:_A,I:{},J:[]};K=inspect.signature(D.func)
		for(B,F)in K.parameters.items():
			C=F.annotation
			if C==inspect._empty:C=str
			G=D._map_type_to_json_schema(C);A[I][B]={H:G}
			if F.default==inspect._empty:A[J].append(B)
			E[B]=G
		A['additionalProperties']=False;return A,E
	def _map_type_to_json_schema(B,py_type):
		A=py_type
		if A in{int,float}:return'number'
		elif A==bool:return'boolean'
		elif A==list:return'array'
		elif A==dict:return _A
		else:return'string'
	def execute(A,*B,**C):
		with asection(f"Executing tool '{A.name}'"):
			try:aprint(f"Arguments: {B}, {C}");D=A.func(*B,**C);aprint(f"Result: {D}")
			except Exception as E:import traceback as F;F.print_exc();aprint(f"Error: {E}");raise E
		return D
	def pretty_string(A):
		C=', '.join([f"{A}: {B}"for(A,B)in A.arg_and_type.items()])
		if len(A.description)>80:B=A.description[:A.description.find('.',80)+1]+'[...]'
		else:B=A.description
		return f"{A.name}({C}) % {B}"
```

==========
Prog. Lang. Code File: toolset.py
Size: 7.68 kilobytes
==========

toolset.py
```py
_A=None
from typing import Callable,List,Optional,Type,Union
from litemind.agent.tools.base_tool import BaseTool
class ToolSet:
	def __init__(B,tools=_A):A=tools;B.tools=A if A else[]
	def add_tool(A,tool):A.tools.append(tool)
	def add_function_tool(B,func,description=_A):from litemind.agent.tools.function_tool import FunctionTool as C;A=C(func,description);B.tools.append(A);return A
	def add_agent_tool(B,agent,description):from litemind.agent.tools.agent_tool import AgentTool as C;A=C(agent,description);B.tools.append(A);return A
	def add_builtin_web_search_tool(B):from litemind.agent.tools.builtin_tools.web_search_tool import BuiltinWebSearchTool as C;A=C();B.tools.append(A);return A
	def add_builtin_mcp_tool(B,server_name,server_url,headers=_A,allowed_tools=_A):from litemind.agent.tools.builtin_tools.mcp_tool import BuiltinMCPTool as C;A=C(server_name,server_url,headers,allowed_tools);B.tools.append(A);return A
	def remove_tool(A,tool):A.tools.remove(tool)
	def has_tool(B,tool):
		A=tool
		if isinstance(A,str):return any(B.name==A for B in B.tools)
		elif isinstance(A,BaseTool):return A in B.tools
		elif isinstance(A,type)and issubclass(A,BaseTool):return any(isinstance(B,A)for B in B.tools)
		return False
	def get_tool(C,name):
		A=name
		if isinstance(A,type)and issubclass(A,BaseTool):A=A.__name__
		for B in C.tools:
			if B.name==A:return B
	def list_tools(A):return A.tools
	def list_builtin_tools(A):return[A for A in A.tools if A.is_builtin()]
	def tool_names(A):return[A.name for A in A.tools]
	def __getitem__(A,item):return A.tools[item]
	def __len__(A):return len(A.tools)
	def __iter__(A):return iter(A.tools)
	def __contains__(A,item):return item in A.tools
	def __delitem__(A,key):del A.tools[key]
	def __setitem__(A,key,value):A.tools[key]=value
	def __iadd__(A,other):A.add_tool(other);return A
	def __add__(A,other):A.add_tool(other);return A
	def __str__(A):return f"ToolSet({[A.name for A in A.tools]})"
	def __repr__(A):return f"ToolSet({A.tools})"
```




==========
Prog. Lang. Code File: builtin_tool.py
Size: 357 bytes
==========

builtin_tool.py
```py
from litemind.agent.tools.base_tool import BaseTool
class BuiltinTool(BaseTool):0
```

==========
Prog. Lang. Code File: mcp_tool.py
Size: 1.93 kilobytes
==========

mcp_tool.py
```py
_A=None
from typing import Any,Dict,List,Optional
from litemind.agent.tools.builtin_tools.builtin_tool import BuiltinTool
class BuiltinMCPTool(BuiltinTool):
	def __init__(A,server_name,server_url,headers=_A,allowed_tools=_A):D=allowed_tools;C=headers;B=server_name;super().__init__(name=f"{B} MCP server tool",description='Built-in MCP server tool');A.server_name=B;A.server_url=server_url;A.headers=C if C is not _A else{};A.allowed_tools=D if D is not _A else[]
	def execute(A,*B,**C):raise RuntimeError(f"{BuiltinMCPTool.__name__} tool cannot be executed directly. It is a placeholder for built-in MCP functionality.")
	def pretty_string(A):return f"{A.name} (Built-in MCP tool, cannot be executed directly)"
```

==========
Prog. Lang. Code File: web_search_tool.py
Size: 2.63 kilobytes
==========

web_search_tool.py
```py
_A='medium'
from typing import Any,List
from litemind.agent.tools.builtin_tools.builtin_tool import BuiltinTool
class BuiltinWebSearchTool(BuiltinTool):
	def __init__(B,search_context_size=_A,max_web_searches=10,allowed_domains=None):
		D=search_context_size;C=max_web_searches;A=allowed_domains;super().__init__(name=BuiltinWebSearchTool.__name__,description='Built-in web search tool')
		if D not in['high',_A,'low']:raise ValueError("search_context_size must be one of 'high', 'medium', or 'low'.")
		B.search_context_size=D
		if not isinstance(C,int)or C<=0:raise ValueError('max_web_searches must be a positive integer.')
		B.max_web_searches=C
		if A is None:A=[]
		if not isinstance(A,list):raise ValueError('allowed_domains must be a list of strings.')
		else:B.allowed_domains=A if A is not None else[]
	def execute(A,*B,**C):raise RuntimeError(f"{BuiltinWebSearchTool.__name__} tool cannot be executed directly. It is a placeholder for built-in web search functionality.")
	def pretty_string(A):return f"{A.name} (Built-in web search tool, cannot be executed directly)"
```




==========
Prog. Lang. Code File: test_agent_tool.py
Size: 7.02 kilobytes
==========

test_agent_tool.py
```py
_G='Order Assistant Agent'
_F='AgentResponse result should not be empty'
_E='AgentResponse result should be a string'
_D='api_class'
_C='assistant'
_B='user'
_A='system'
import pytest
from litemind import API_IMPLEMENTATIONS
from litemind.agent.agent import Agent
from litemind.agent.messages.message import Message
from litemind.agent.tools.agent_tool import AgentTool
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.model_features import ModelFeatures
@pytest.mark.parametrize(_D,API_IMPLEMENTATIONS)
def test_tool_agent_translation(api_class):
	G="Agent's conversation should have 3 messages: system, prompt, and assistant's response";F='bonjour';C=api_class;D=C()
	if not D.has_model_support_for(ModelFeatures.TextGeneration):pytest.skip(f"Skipping test for {C.__name__} as no text model is available.")
	H=Message(role=_A,text='You are a translator that translates English sentences to French.');A=Agent(api=D);A+=H;E=AgentTool(A,'Translation agent from English to French',has_memory=True);I="Translate the following sentence: 'Hello, how are you?'";B=E.execute(prompt=I);assert isinstance(B,str),_E;assert B,_F;assert'comment'in B.lower()or F in B.lower(),'AgentTool response should be a French translation of the input';assert len(A.conversation)==3,G;assert A.conversation[0].role==_A;assert A.conversation[1].role==_B;assert A.conversation[2].role==_C;assert F in B.lower(),f"Expected translation to contain 'Bonjour' but got '{B}'";assert len(E.agent.conversation)==3,G
@pytest.mark.parametrize(_D,API_IMPLEMENTATIONS)
def test_tool_agent(api_class):
	B=api_class;C=B()
	if not C.has_model_support_for(ModelFeatures.TextGeneration):pytest.skip(f"Skipping test for {B.__name__} as no text model is available.")
	E=Agent(api=C,name=_G);D=AgentTool(E,'Assistant agent that processes prompts');assert D.description=='Assistant agent that processes prompts.';F='What is the status of my order?';A=D.execute(prompt=F);assert isinstance(A,str),_E;assert A,_F;assert'status'in A.lower()or'order'in A.lower(),'AgentTool response should address the order status'
@pytest.mark.parametrize(_D,API_IMPLEMENTATIONS)
def test_tool_agent_with_internal_tool(api_class):
	D=api_class;C=D()
	if not C.has_model_support_for(ModelFeatures.TextGeneration):pytest.skip(f"Skipping test for {D.__name__} as no text model is available.")
	A=Agent(api=C,name=_G)
	def H(order_id):return f"The status of order {order_id} is: Shipped"
	E=ToolSet();E.add_function_tool(H,'Check the status of an order by ID');A.toolset=E;A+=Message(role=_A,text='You are an assistant that provides order status updates.');I=AgentTool(A,'Processes order inquiries, e.g. can check the status of an order by ID');B=Agent(api=C);B+=Message(role=_A,text='You are an assistant that provides order status updates and can write poems.');B.toolset=ToolSet();B.toolset.add_tool(I);J='Can you tell me the status of order #12345?';F=B(J);assert isinstance(F,list),'AgentResponse result should be a list';G=str(F).lower();assert'shipped'in G and'12345'in G or'order','AgentTool response should include the status provided by FunctionTool';assert len(A.conversation)==5,"Agent's conversation should have 3 messages: system, user and assistant's response";assert A.conversation[0].role==_A;assert A.conversation[1].role==_B;assert A.conversation[2].role==_C;assert A.conversation[3].role==_B or A.conversation[3].role=='tool';assert A.conversation[4].role==_C
```

==========
Prog. Lang. Code File: test_function_tool.py
Size: 4.46 kilobytes
==========

test_function_tool.py
```py
_M='Function with default parameter'
_L='Function with no parameters'
_K='string'
_J='number'
_I='No parameters here!'
_H=False
_G='param2'
_F='object'
_E='additionalProperties'
_D='required'
_C='properties'
_B='param1'
_A='type'
from litemind.agent.tools.function_tool import FunctionTool
def sample_function_one(param1,param2):return f"Received {param1} and {param2}"
def sample_function_no_params():return _I
def sample_function_with_defaults(param1,param2='default'):return f"Received {param1} and {param2}"
def test_tool_initialization():A=FunctionTool(func=sample_function_one,description='Test function with two parameters');B={_A:_F,_C:{_B:{_A:_J},_G:{_A:_K}},_D:[_B,_G],_E:_H};assert A.arguments_schema==B,'Tool parameters schema does not match the expected format'
def test_tool_initialization_automatic_description():A=FunctionTool(func=sample_function_one);assert A.description=='Sample function that returns a formatted string from two integers.','Description does not match function docstring'
def test_tool_with_no_parameters():A=FunctionTool(func=sample_function_no_params,description=_L);B={_A:_F,_C:{},_D:[],_E:_H};assert A.arguments_schema==B,'Expected empty parameters for function with no parameters'
def test_tool_with_default_parameter():A=FunctionTool(func=sample_function_with_defaults,description=_M);B={_A:_F,_C:{_B:{_A:_J},_G:{_A:_K}},_D:[_B],_E:_H};assert A.arguments_schema==B,'Expected param2 to be optional due to default value'
def test_tool_execution_with_parameters():A=FunctionTool(func=sample_function_one,description='Test function with parameters');B=A.execute(param1=42,param2='example');assert B=='Received 42 and example','Execution result mismatch for sample_function_one'
def test_tool_execution_no_parameters():A=FunctionTool(func=sample_function_no_params,description=_L);B=A.execute();assert B==_I,'Execution result mismatch for sample_function_no_params'
def test_tool_execution_with_default_parameters():A=FunctionTool(func=sample_function_with_defaults,description=_M);B=A.execute(param1=100);assert B=='Received 100 and default','Default parameter not applied correctly';C=A.execute(param1=100,param2='custom');assert C=='Received 100 and custom','Parameter override not applied correctly'
```

==========
Prog. Lang. Code File: test_toolset.py
Size: 4.74 kilobytes
==========

test_toolset.py
```py
_C='The tool description should match the description provided'
_B='sample_function'
_A='Sample function that squares a number'
import pytest
from litemind import API_IMPLEMENTATIONS
from litemind.agent.tools.agent_tool import AgentTool
from litemind.agent.tools.function_tool import FunctionTool
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.model_features import ModelFeatures
def sample_function(x):return x*x
def test_toolset_initialization_and_add_tool():A=ToolSet();assert A.list_tools()==[],'ToolSet should be initialized empty';B=FunctionTool(sample_function,_A);A.add_tool(B);assert len(A.list_tools())==1,'ToolSet should contain one tool after adding';assert A.list_tools()[0]==B,'The added tool should be the FunctionTool instance'
def test_toolset_add_function_tool():B=ToolSet();B.add_function_tool(sample_function,_A);A=B.get_tool(_B);assert isinstance(A,FunctionTool),'The tool should be an instance of FunctionTool';assert A.description==_A,_C;C=A.execute(x=3);assert C==9,'The FunctionTool should return the square of the input'
@pytest.mark.parametrize('api_class',API_IMPLEMENTATIONS)
def test_toolset_add_agent_tool(api_class):
	E='agent_tool';B=api_class;from litemind.agent.agent import Agent;C=B()
	if not C.has_model_support_for(ModelFeatures.TextGeneration):pytest.skip(f"Skipping test for {B.__name__} as no text model is available.")
	F=Agent(api=C,name=E);D=ToolSet();D.add_agent_tool(F,'Sample agent tool for testing');A=D.get_tool(E);assert isinstance(A,AgentTool),'The tool should be an instance of AgentTool';assert A.description=='Sample agent tool for testing.',_C;G=A.execute(prompt="Translate 'Hello' to French.");assert'bonjour'in G.lower(),"The response should contain 'bonjour' as part of the translation"
def test_toolset_get_tool():A=ToolSet();A.add_function_tool(sample_function,_A);B=A.get_tool(_B);assert B is not None,'get_tool should return the tool if it exists';assert B.name==_B,'The tool name should match the function name';assert isinstance(B,FunctionTool),'The retrieved tool should be an instance of FunctionTool';C=A.get_tool('non_existent_tool');assert C is None,'get_tool should return None for a non-existent tool'
def test_toolset_list_tools():B=ToolSet();B.add_function_tool(sample_function,_A);A=B.list_tools();assert len(A)==1,'ToolSet should contain one tool';assert isinstance(A[0],FunctionTool),'The first tool in the list should be a FunctionTool';assert A[0].name==_B,"The tool name should match 'sample_function'"
```




==========
Prog. Lang. Code File: base_api.py
Size: 21.45 kilobytes
==========

base_api.py
```py
_A=None
from abc import ABC,abstractmethod
from typing import List,Optional,Sequence,Type,Union
from PIL.Image import Image
from pydantic import BaseModel
from litemind.agent.messages.message import Message
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.callbacks.callback_manager import CallbackManager
from litemind.apis.model_features import ModelFeatures
from litemind.media.media_base import MediaBase
from litemind.utils.random_projector import DeterministicRandomProjector
class BaseApi(ABC):
	def __init__(A,callback_manager=_A,**C):
		B=callback_manager
		if B is not _A:A.callback_manager=B
		else:A.callback_manager=CallbackManager()
	@abstractmethod
	def check_availability_and_credentials(self,api_key=_A):0
	@abstractmethod
	def list_models(self,features=_A,non_features=_A,media_types=_A):0
	@abstractmethod
	def get_best_model(self,features=_A,non_features=_A,media_types=_A,exclusion_filters=_A):0
	@abstractmethod
	def has_model_support_for(self,features,media_types=_A,model_name=_A):0
	@abstractmethod
	def get_model_features(self,model_name):0
	@abstractmethod
	def max_num_input_tokens(self,model_name=_A):0
	@abstractmethod
	def max_num_output_tokens(self,model_name=_A):0
	@abstractmethod
	def count_tokens(self,text,model_name=_A):0
	@abstractmethod
	def generate_text(self,messages,model_name=_A,temperature=.0,max_num_output_tokens=_A,toolset=_A,use_tools=True,response_format=_A,**A):0
	@abstractmethod
	def generate_audio(self,text,voice=_A,audio_format=_A,model_name=_A,**A):0
	@abstractmethod
	def generate_image(self,positive_prompt,negative_prompt=_A,model_name=_A,image_width=512,image_height=512,preserve_aspect_ratio=True,allow_resizing=True,**A):0
	@abstractmethod
	def generate_video(self,description,model_name=_A,**A):0
	def embed_texts(A,texts,model_name=_A,dimensions=512,**B):0
	def _reduce_embeddings_dimension(C,embeddings,reduced_dim):A=embeddings;B=DeterministicRandomProjector(original_dim=len(A[0]),reduced_dim=reduced_dim);return B.transform(A)
	@abstractmethod
	def embed_images(self,image_uris,model_name=_A,dimensions=512,**A):0
	@abstractmethod
	def embed_audios(self,audio_uris,model_name=_A,dimensions=512,**A):0
	@abstractmethod
	def embed_videos(self,video_uris,model_name=_A,dimensions=512,**A):0
	@abstractmethod
	def embed_documents(self,document_uris,model_name=_A,dimensions=512,**A):0
	@abstractmethod
	def transcribe_audio(self,audio_uri,model_name=_A,**A):0
	@abstractmethod
	def describe_image(self,image_uri,system='You are a helpful AI assistant that can describe and analyse images.',query='Here is an image, please carefully describe it completely and in detail.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):0
	@abstractmethod
	def describe_audio(self,audio_uri,system='You are a helpful AI assistant that can describe and analyse audio.',query='Here is an audio file, please carefully describe its contents in detail. If it is speech, please transcribe completely and accurately.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):0
	@abstractmethod
	def describe_video(self,video_uri,system='You are a helpful AI assistant that can describe/analyse videos.',query='Here is a video file, please carefully and completely describe it in detail.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):0
	@abstractmethod
	def describe_document(self,document_uri,system='You are a helpful AI assistant that can describe/analyse documents.',query='Here is a document, please carefully and completely describe it in detail.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):0
```

==========
Prog. Lang. Code File: combined_api.py
Size: 29.98 kilobytes
==========

combined_api.py
```py
_I='number_of_tries'
_H='max_output_tokens'
_G='temperature'
_F='query'
_E='system'
_D=True
_C='dimensions'
_B='model_name'
_A=None
from typing import Dict,List,Optional,Sequence,Type,Union
from arbol import aprint
from PIL.Image import Image
from pydantic import BaseModel
from litemind.agent.messages.message import Message
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import BaseApi
from litemind.apis.callbacks.callback_manager import CallbackManager
from litemind.apis.default_api import DefaultApi
from litemind.apis.exceptions import APIError
from litemind.apis.model_features import ModelFeatures
from litemind.media.media_base import MediaBase
class CombinedApi(DefaultApi):
	def __init__(B,apis=_A,api_keys=_A,callback_manager=_A):
		F=callback_manager;E=api_keys;C=apis;super().__init__(callback_manager=F)
		if C is _A:
			from litemind.apis.providers.anthropic.anthropic_api import AnthropicApi as K;from litemind.apis.providers.google.google_api import GeminiApi as L;from litemind.apis.providers.ollama.ollama_api import OllamaApi as M;from litemind.apis.providers.openai.openai_api import OpenAIApi as N;O=[N,K,M,L];C=[]
			for D in O:
				try:
					G=D()
					if G.check_availability_and_credentials():
						if len(G.list_models())>0:C.append(G)
						else:aprint(f"API {D.__name__} does not provide any models. Removing it from the list.")
					else:aprint(f"API {D.__name__} is not available. Removing it from the list.")
				except Exception as H:aprint(f"API {D.__name__} could not be instantiated: {H}. Removing it from the list.")
		if F:
			for A in C:A.callback_manager.add_callbacks(F)
		B.apis=[];B.model_to_api={}
		for A in C:
			I=A.__class__.__name__;P=_A if not E or I not in E else E.get(I)
			if not A.check_availability_and_credentials(api_key=P):aprint(f"API {I} not added due to unavailability or invalid credentials.");continue
			try:
				Q=A.list_models()
				for J in Q:
					if J not in B.model_to_api:B.model_to_api[J]=A
				B.apis.append(A)
			except Exception as H:aprint(f"API {A.__class__.__name__} not added due to error: {H}")
		if not B.apis:raise ValueError('No API available.')
	def check_availability_and_credentials(A,api_key=_A):B=len(A.model_to_api)>0;A.callback_manager.on_availability_check(B);return B
	def list_models(C,features=_A,non_features=_A,media_types=_A):
		D=non_features;B=features
		try:
			B=ModelFeatures.normalise(B);D=ModelFeatures.normalise(D);A=list(C.model_to_api.keys());A+=super().list_models()
			if B:A=C._filter_models(A,features=B,non_features=D,media_types=media_types)
			C.callback_manager.on_model_list(A);return A
		except Exception:raise APIError('Error fetching model list from OpenAI.')
	def get_best_model(B,features=_A,non_features=_A,media_types=_A,exclusion_filters=_A):
		D=exclusion_filters;C=features
		for E in B.apis:
			try:
				A=E.get_best_model(features=C,non_features=non_features,media_types=media_types,exclusion_filters=D)
				if A is _A or A not in B.model_to_api:continue
				F={'features':C,'exclusion_filters':D};B.callback_manager.on_best_model_selected(A,**F);return A
			except Exception as G:aprint(f"Error getting best model from {E.__class__.__name__}: {G}")
	def has_model_support_for(C,features,media_types=_A,model_name=_A):
		D=media_types;B=features;A=model_name;B=ModelFeatures.normalise(B)
		if A is _A:A=C.get_best_model(features=B,media_types=D)
		if A is _A:return False
		if super().has_model_support_for(features=B,media_types=D,model_name=A):return _D
		if A not in C.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		E=C.model_to_api[A];F=E.has_model_support_for(features=B,media_types=D,model_name=A);return F
	def max_num_input_tokens(B,model_name=_A):
		A=model_name
		if A is _A:A=B.get_best_model()
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		C=B.model_to_api[A];return C.max_num_input_tokens(model_name=A)
	def max_num_output_tokens(B,model_name=_A):
		A=model_name
		if A is _A:A=B.get_best_model()
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		C=B.model_to_api[A];return C.max_num_output_tokens(model_name=A)
	def count_tokens(B,text,model_name=_A):
		A=model_name
		if A is _A:A=B.get_best_model()
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		C=B.model_to_api[A];return C.count_tokens(text,A)
	def generate_text(B,messages,model_name=_A,temperature=.0,max_num_output_tokens=_A,toolset=_A,use_tools=_D,response_format=_A,**E):
		C=messages;A=model_name
		if A is _A:A=B.get_best_model(ModelFeatures.TextGeneration)
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		F=B.model_to_api[A];D=F.generate_text(messages=C,model_name=A,temperature=temperature,max_num_output_tokens=max_num_output_tokens,toolset=toolset,use_tools=use_tools,response_format=response_format);B.callback_manager.on_text_generation(C,response=D,**E);return D
	def generate_audio(B,text,voice=_A,audio_format=_A,model_name=_A,**E):
		D=audio_format;C=voice;A=model_name
		if A is _A:A=B.get_best_model(ModelFeatures.AudioGeneration)
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		G=B.model_to_api[A];F=G.generate_audio(text,C,D,A);E.update({'voice':C,'audio_format':D,_B:A});B.callback_manager.on_audio_generation(text,F,**E);return F
	def generate_image(B,positive_prompt,negative_prompt=_A,model_name=_A,image_width=512,image_height=512,preserve_aspect_ratio=_D,allow_resizing=_D,**I):
		H=allow_resizing;G=preserve_aspect_ratio;F=image_height;E=image_width;D=negative_prompt;C=positive_prompt;A=model_name
		if A is _A:A=B.get_best_model(ModelFeatures.ImageGeneration)
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		K=B.model_to_api[A];J=K.generate_image(positive_prompt=C,negative_prompt=D,model_name=A,image_width=E,image_height=F,preserve_aspect_ratio=G,allow_resizing=H);I.update({'positive_prompt':C,'negative_prompt':D,_B:A,'image_width':E,'image_height':F,'preserve_aspect_ratio':G,'allow_resizing':H});B.callback_manager.on_image_generation(C,J,**I);return J
	def embed_texts(B,texts,model_name=_A,dimensions=512,**E):
		D=dimensions;C=texts;A=model_name
		if A is _A:A=B.get_best_model(ModelFeatures.TextEmbeddings)
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		G=B.model_to_api[A];F=G.embed_texts(C,A,D);E.update({_B:A,_C:D});B.callback_manager.on_text_embedding(C,F,**E);return F
	def embed_images(B,image_uris,model_name=_A,dimensions=512,**E):
		D=dimensions;C=image_uris;A=model_name
		if A is _A:A=B.get_best_model(ModelFeatures.ImageEmbeddings)
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		G=B.model_to_api[A];F=G.embed_images(image_uris=C,model_name=A,dimensions=D);E.update({_B:A,_C:D});B.callback_manager.on_image_embedding(C,F,**E);return F
	def embed_audios(B,audio_uris,model_name=_A,dimensions=512,**E):
		D=dimensions;C=audio_uris;A=model_name
		if A is _A:A=B.get_best_model(ModelFeatures.AudioEmbeddings)
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		G=B.model_to_api[A];F=G.embed_audios(C,A,D);E.update({_B:A,_C:D});B.callback_manager.on_audio_embedding(C,F,**E);return F
	def embed_videos(B,video_uris,model_name=_A,dimensions=512,**E):
		D=dimensions;C=video_uris;A=model_name
		if A is _A:A=B.get_best_model(ModelFeatures.VideoEmbeddings)
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		G=B.model_to_api[A];F=G.embed_videos(C,A,D);E.update({_B:A,_C:D});B.callback_manager.on_video_embedding(C,F,**E);return F
	def embed_documents(B,document_uris,model_name=_A,dimensions=512,**E):
		D=dimensions;C=document_uris;A=model_name
		if A is _A:A=B.get_best_model(ModelFeatures.DocumentEmbeddings)
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		G=B.model_to_api[A];F=G.embed_documents(C,A,D);E.update({_B:A,_C:D});B.callback_manager.on_document_embedding(C,F,**E);return F
	def describe_image(B,image_uri,system='You are a helpful AI assistant that can describe/analyse images.',query='Here is an image, please carefully describe it in detail.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):
		H=number_of_tries;G=max_output_tokens;F=temperature;E=query;D=system;C=image_uri;A=model_name
		if A is _A:A=B.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Image])
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		J=B.model_to_api[A];I=J.describe_image(C,D,E,A,F,G,H);K={_E:D,_F:E,_B:A,_G:F,_H:G,_I:H};B.callback_manager.on_image_description(C,I,**K);return I
	def describe_audio(B,audio_uri,system='You are a helpful AI assistant that can describe/analyse audio.',query='Here is an audio file, please carefully describe it in detail. If it is speach, please transcribe accurately.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):
		H=number_of_tries;G=max_output_tokens;F=temperature;E=query;D=system;C=audio_uri;A=model_name
		if A is _A:A=B.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Audio])
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		J=B.model_to_api[A];I=J.describe_audio(C,D,E,A,F,G,H);K={_E:D,_F:E,_B:A,_G:F,_H:G,_I:H};B.callback_manager.on_audio_description(C,I,**K);return I
	def transcribe_audio(B,audio_uri,model_name=_A,**D):
		C=audio_uri;A=model_name
		if A is _A:A=B.get_best_model(features=[ModelFeatures.AudioTranscription])
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		F=B.model_to_api[A];E=F.transcribe_audio(audio_uri=C,model_name=A,**D);G={_B:A,'model_kwargs':D};B.callback_manager.on_audio_transcription(E,C,**G);return E
	def describe_video(B,video_uri,system='You are a helpful AI assistant that can describe/analyse videos.',query='Here is a video file, please carefully describe it in detail.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):
		H=number_of_tries;G=max_output_tokens;F=temperature;E=query;D=system;C=video_uri;A=model_name
		if A is _A:A=B.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Video])
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		J=B.model_to_api[A];I=J.describe_video(C,D,E,A,F,G,H);K={_E:D,_F:E,_B:A,_G:F,_H:G,_I:H};B.callback_manager.on_video_description(C,I,**K);return I
	def describe_document(B,document_uri,system='You are a helpful AI assistant that can describe/analyse documents.',query='Here is a document file, please carefully describe it in detail.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):
		H=number_of_tries;G=max_output_tokens;F=temperature;E=query;D=system;C=document_uri;A=model_name
		if A is _A:A=B.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Image])
		if A not in B.model_to_api:raise ValueError(f"Model '{A}' not found in any API.")
		J=B.model_to_api[A];I=J.describe_document(C,D,E,A,F,G,H);K={_E:D,_F:E,_B:A,_G:F,_H:G,_I:H};B.callback_manager.on_document_description(C,I,**K);return I
```

==========
Prog. Lang. Code File: default_api.py
Size: 56.08 kilobytes
==========

default_api.py
```py
_M='whisper-local'
_L='fastembed'
_K='query'
_J='number_of_tries'
_I='max_output_tokens'
_H='temperature'
_G='user'
_F='dimensions'
_E='system'
_D='model_name'
_C=True
_B=False
_A=None
import copy,json
from functools import lru_cache
from typing import List,Optional,Sequence,Set,Type,Union
from arbol import aprint,asection
from pydantic import BaseModel
from litemind.agent.messages.actions.tool_call import ToolCall
from litemind.agent.messages.message import Message
from litemind.agent.tools.base_tool import BaseTool
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import BaseApi
from litemind.apis.callbacks.callback_manager import CallbackManager
from litemind.apis.exceptions import FeatureNotAvailableError
from litemind.apis.model_features import ModelFeatures
from litemind.media.conversion.converters.media_converter_delegated_callables import MediaConverterApi
from litemind.media.conversion.media_converter import MediaConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_action import Action
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
from litemind.utils.fastembed_embeddings import fastembed_text,is_fastembed_available
from litemind.utils.random_projector import DeterministicRandomProjector
from litemind.utils.whisper_transcribe_audio import is_local_whisper_available,transcribe_audio_with_local_whisper
class DefaultApi(BaseApi):
	def __init__(A,allow_media_conversions=_C,allow_media_conversions_with_models=_C,callback_manager=_A):
		B=allow_media_conversions;super().__init__(callback_manager=callback_manager);A.allow_media_conversions=B;A.media_converter=MediaConverter()
		if B:
			A.media_converter.add_default_converters()
			if allow_media_conversions_with_models:C=MediaConverterApi(api=A);A.media_converter.add_media_converter(C)
	def check_availability_and_credentials(A,api_key=_A):A.callback_manager.on_availability_check(_C);return _C
	def list_models(D,features=_A,non_features=_A,media_types=_A):
		C=non_features;B=features;B=ModelFeatures.normalise(B);C=ModelFeatures.normalise(C);A=[]
		if is_fastembed_available():A.append(_L)
		if is_local_whisper_available():A.append(_M)
		if B:A=D._filter_models(A,features=B,non_features=C)
		D.callback_manager.on_model_list(A);return A
	def get_best_model(C,features=_A,non_features=_A,media_types=_A,exclusion_filters=_A):
		E=exclusion_filters;D=non_features;A=features;A=ModelFeatures.normalise(A);D=ModelFeatures.normalise(D);B=C.list_models();B=C._filter_models(B,features=A,non_features=D,media_types=media_types,exclusion_filters=E)
		if len(B)==0:return
		F=B[0];G={'features':A,'exclusion_filters':E};C.callback_manager.on_best_model_selected(F,**G);return F
	def _filter_models(E,model_list,features,non_features=_A,media_types=_A,exclusion_filters=_A):
		F=model_list;C=non_features;B=features;A=exclusion_filters;G=[]
		if not B:return F
		B=ModelFeatures.normalise(B);C=ModelFeatures.normalise(C)
		if isinstance(A,str):A=[A]
		for D in F:
			if A and any([A in D for A in A]):continue
			if C and any([E.has_model_support_for(model_name=D,features=A)for A in C]):continue
			if E.has_model_support_for(model_name=D,features=B,media_types=media_types):G.append(D)
		return G
	def has_model_support_for(A,features,media_types=_A,model_name=_A):
		F=media_types;E=features;C=model_name
		if F is not _A:
			D=ModelFeatures.get_features_needed_for_media_types(media_types=F)
			if ModelFeatures.Image in D:
				D.remove(ModelFeatures.Image)
				if not A.has_model_support_for(features=ModelFeatures.Image,model_name=C)and not A.has_model_support_for(features=ModelFeatures.ImageConversion,model_name=C):return _B
			if ModelFeatures.Audio in D:
				D.remove(ModelFeatures.Audio)
				if not A.has_model_support_for(features=ModelFeatures.Audio,model_name=C)and not A.has_model_support_for(features=ModelFeatures.AudioConversion,model_name=C):return _B
			if ModelFeatures.Video in D:
				D.remove(ModelFeatures.Video)
				if not A.has_model_support_for(features=ModelFeatures.Video,model_name=C)and not A.has_model_support_for(features=ModelFeatures.VideoConversion,model_name=C):return _B
			if ModelFeatures.Document in D:
				D.remove(ModelFeatures.Document)
				if not A.has_model_support_for(features=ModelFeatures.Document,model_name=C)and not A.has_model_support_for(features=ModelFeatures.DocumentConversion,model_name=C):return _B
		E=ModelFeatures.normalise(E)
		if super().has_model_support_for(features=E,model_name=C):return _C
		for B in E:
			if B==ModelFeatures.AudioTranscription:
				if not is_local_whisper_available()or C!=_M:return _B
			elif B==ModelFeatures.ImageConversion:
				if not A.media_converter.can_convert_within(source_media_type=Image,allowed_media_types={Text}):return _B
			elif B==ModelFeatures.AudioConversion:
				if not A.media_converter.can_convert_within(source_media_type=Audio,allowed_media_types={Text}):return _B
			elif B==ModelFeatures.DocumentConversion:
				if not(A.media_converter.can_convert_within(source_media_type=Document,allowed_media_types={Text,Image})and A.has_model_support_for(ModelFeatures.Image)or A.media_converter.can_convert_within(source_media_type=Document,allowed_media_types={Text})):return _B
			elif B==ModelFeatures.VideoConversion:
				if not(A.media_converter.can_convert_within(source_media_type=Video,allowed_media_types={Text,Image,Audio})and A.has_model_support_for([ModelFeatures.Image,ModelFeatures.Audio])or A.media_converter.can_convert_within(source_media_type=Video,allowed_media_types={Text,Image})and A.has_model_support_for([ModelFeatures.Image])or A.media_converter.can_convert_within(source_media_type=Video,allowed_media_types={Text,Audio})and A.has_model_support_for([ModelFeatures.Audio])or A.media_converter.can_convert_within(source_media_type=Video,allowed_media_types={Text})):return _B
			elif B==ModelFeatures.TextEmbeddings or B==ModelFeatures.ImageEmbeddings or B==ModelFeatures.AudioEmbeddings or B==ModelFeatures.VideoEmbeddings or B==ModelFeatures.DocumentEmbeddings:
				if C is not _A and C!=_L:return _B
				if not is_fastembed_available():aprint('Text Embeddings feature: fastembed is not available! \n Install with: pip install fastembed');return _B
				if B==ModelFeatures.ImageEmbeddings and not A.has_model_support_for(features=ModelFeatures.TextGeneration,media_types=[Image]):aprint('Image Embeddings feature: model does not support images!');return _B
				elif B==ModelFeatures.AudioEmbeddings and not A.has_model_support_for(features=ModelFeatures.TextGeneration,media_types=[Audio]):aprint('Audio Embeddings feature: model does not support audio!');return _B
				elif B==ModelFeatures.VideoEmbeddings and not A.has_model_support_for(features=ModelFeatures.TextGeneration,media_types=[Video]):aprint('Video Embeddings feature: model does not support video!');return _B
				elif B==ModelFeatures.DocumentEmbeddings and not A.has_model_support_for(features=ModelFeatures.TextGeneration,media_types=[Document]):aprint('Document Embeddings feature: model does not support documents!');return _B
			else:return _B
		return _C
	def get_model_features(C,model_name):
		A=[]
		for B in ModelFeatures:
			if C.has_model_support_for(model_name=model_name,features=B):A.append(B)
		return A
	@lru_cache
	def _get_allowed_media_types_for_text_generation(self,model_name):B=self.get_model_features(model_name);A=ModelFeatures.get_supported_media_types(B);A=set(A);return A
	def max_num_input_tokens(A,model_name=_A):return 1000
	def max_num_output_tokens(A,model_name=_A):return 1000
	def count_tokens(A,text,model_name=_A):return int(len(text.split())*1.33)
	def generate_text(E,messages,model_name=_A,temperature=.0,max_num_output_tokens=_A,toolset=_A,use_tools=_C,response_format=_A,**F):
		D=toolset;C=temperature;B=messages;A=max_num_output_tokens
		if not isinstance(B,list):raise ValueError('Messages must be a list of Message objects.')
		if not all(isinstance(A,Message)for A in B):raise ValueError('All messages must be Message objects.')
		if not B:raise ValueError('Messages list cannot be empty.')
		if not isinstance(C,(int,float)):raise ValueError('Temperature must be a number.')
		if C<0 or C>1:raise ValueError('Temperature must be between 0 and 1.')
		if A is not _A and not isinstance(A,int):raise ValueError('max_num_output_tokens must be an integer.')
		if A is not _A and A<=0:raise ValueError('max_num_output_tokens must be greater than 0.')
		if D is not _A and not isinstance(D,ToolSet):raise ValueError('toolset must be a ToolSet object.')
	def _preprocess_messages(B,messages,allowed_media_types=_A,exclude_extensions=_A,deepcopy=_C):
		A=messages
		if deepcopy:A=copy.deepcopy(A)
		A=B.media_converter.convert(messages=A,allowed_media_types=allowed_media_types,exclude_extensions=exclude_extensions);return A
	def _get_best_model_for_text_generation(C,messages,toolset,response_format):
		A=[ModelFeatures.TextGeneration]
		if toolset:A.append(ModelFeatures.Tools)
		if response_format:A.append(ModelFeatures.StructuredTextGeneration)
		D=Message.list_media_types_in(messages);B=C.get_best_model(features=A,media_types=D)
		if B is _A:raise FeatureNotAvailableError(f"No suitable model with features: {A}")
		return B
	def _process_tool_calls(L,response,messages,new_messages,preprocessed_messages,toolset,set_preprocessed=_B):
		G=toolset;E=preprocessed_messages;A=Message();messages.append(A)
		if set_preprocessed:E.clear();E.append(A)
		else:E.append(A)
		new_messages.append(A);I=[A.media.get_content()for A in response if A.has_type(Action)]
		for B in I:
			if isinstance(B,ToolCall):
				C=B.tool_name;H=G.get_tool(C)if G else _A;F=B.arguments
				if H:
					try:
						D=H.execute(**F)
						if not isinstance(D,str):D=json.dumps(D,default=str)
					except Exception as J:D=f"Function '{C}' error: {J}"
					A.append_tool_use(tool_name=C,arguments=F,result=D,id=B.id)
				else:K=f"(Tool '{C}' use requested, but tool not found.)";A.append_tool_use(tool_name=C,arguments=F,result=K,id=B.id)
	def generate_audio(A,text,voice=_A,audio_format=_A,model_name=_A,**B):raise NotImplementedError('Audio generation is not supported by this API.')
	def generate_image(A,positive_prompt,negative_prompt=_A,model_name=_A,image_width=512,image_height=512,preserve_aspect_ratio=_C,allow_resizing=_C,**B):raise NotImplementedError('Image generation is not supported by this API.')
	def generate_video(A,description,model_name=_A,**B):raise NotImplementedError('Video generation is not supported by this API.')
	def embed_texts(D,texts,model_name=_A,dimensions=512,**C):
		E=dimensions;B=texts;A=model_name
		if A is _A:A=D.get_best_model(features=ModelFeatures.TextEmbeddings)
		if not A==_L:raise FeatureNotAvailableError(f"Model '{A}' does not support text embedding.")
		if not is_fastembed_available():raise FeatureNotAvailableError('Text Embedding feature: fastembed is not available! \n Install with: pip install fastembed')
		B=list(B);F=fastembed_text(texts=B,dimensions=E,**C);C.update({_D:A,_F:E});D.callback_manager.on_text_embedding(texts=B,embeddings=F,**C);return F
	def _reduce_embeddings_dimension(C,embeddings,reduced_dim):A=embeddings;B=DeterministicRandomProjector(original_dim=len(A[0]),reduced_dim=reduced_dim);return B.transform(A)
	def embed_images(A,image_uris,model_name=_A,dimensions=512,**C):
		F=dimensions;E=image_uris;B=model_name
		if B is _A:B=A.get_best_model(features=ModelFeatures.ImageEmbeddings)
		G=A.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Image])
		if not G:raise FeatureNotAvailableError("Can't find a text generation model that supports images or image conversion.")
		D=[]
		for I in E:J=A.describe_image(image_uri=I,model_name=G,query='Please describe the following image.',**C);D.append(J)
		H=A.embed_texts(texts=D,model_name=B,dimensions=F);C.update({_D:B,'image_descriptions':D,_F:F});A.callback_manager.on_image_embedding(image_uris=E,embeddings=H,**C);return H
	def embed_audios(A,audio_uris,model_name=_A,dimensions=512,**C):
		H=dimensions;G=audio_uris;B=model_name
		if B is _A:B=A.get_best_model(features=[ModelFeatures.TextEmbeddings])
		D=A.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Audio])
		if not D:E=A.get_best_model([ModelFeatures.AudioConversion])
		else:E=_A
		F=[]
		for I in G:
			if D is not _A:J=A.describe_audio(audio_uri=I,model_name=D,query='Please describe the following audio.',**C)
			elif E is not _A:J=A.transcribe_audio(audio_uri=I,model_name=E,**C)
			else:raise FeatureNotAvailableError("Can't find a model that supports audio in text generation or an audio transcription model.")
			F.append(J)
		K=A.embed_texts(texts=F,model_name=B,dimensions=H);C.update({_D:B,'audio_descriptions':F,_F:H});A.callback_manager.on_audio_embedding(audio_uris=G,embeddings=K,**C);return K
	def embed_videos(A,video_uris,model_name=_A,dimensions=512,**C):
		F=dimensions;E=video_uris;B=model_name
		if B is _A:B=A.get_best_model(features=[ModelFeatures.TextEmbeddings])
		G=A.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Video])
		if not G:raise FeatureNotAvailableError("Can't find a text generation model that supports video.")
		D=[]
		for I in E:J=A.describe_video(video_uri=I,model_name=G,query='Please describe the following video.',**C);D.append(J)
		H=A.embed_texts(texts=D,model_name=B,dimensions=F);C.update({_D:B,'video_descriptions':D,_F:F});A.callback_manager.on_video_embedding(video_uris=E,embeddings=H,**C);return H
	def embed_documents(A,document_uris,model_name=_A,dimensions=512,**C):
		F=dimensions;E=document_uris;B=model_name
		if B is _A:B=A.get_best_model(features=[ModelFeatures.TextEmbeddings])
		G=A.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Document])
		if not G:raise FeatureNotAvailableError("Can't find a text generation model that supports documents.")
		H=[]
		for I in E:J=A.describe_document(document_uri=I,model_name=G,query='Please describe the following document.',**C);H.append(J)
		D=A.embed_texts(texts=H,model_name=B,dimensions=F);C.update({_D:B,'document_embeddings':D,_F:F});A.callback_manager.on_document_embedding(document_uris=E,embeddings=D,**C);return D
	def transcribe_audio(B,audio_uri,model_name=_A,**D):
		C=audio_uri;A=model_name
		if A is _A:A=B.get_best_model(features=[ModelFeatures.AudioTranscription])
		if A is _A:raise FeatureNotAvailableError('No model available for audio transcription.')
		if A==_M:
			if not is_local_whisper_available():raise FeatureNotAvailableError('Audio Transcription feature: whisper is not available! \n Install with: pip install openai-whisper')
			E=transcribe_audio_with_local_whisper(audio_uri=C,**D);F={_D:A,'model_kwargs':D};B.callback_manager.on_audio_transcription(audio_uri=C,transcription=E,**F);return E
		else:raise NotImplementedError(f"Unknown transcription model: '{A}'")
	def describe_image(E,image_uri,system='You are a helpful AI assistant that can describe and analyse images.',query='Here is an image, please carefully describe it completely and in detail.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):
		L=number_of_tries;K=temperature;J=system;G=query;F=image_uri;D=max_output_tokens;A=model_name
		with asection(f"Asking model {A} to describe a given image: '{F}':"):
			aprint(f"Query: '{G}'");aprint(f"Model: '{A}'");aprint(f"Max tokens: '{D}'")
			if A is _A:A=E.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Image])
			if A is _A:raise FeatureNotAvailableError(f"Model '{A}' does not support images.")
			if D is _A:D=E.max_num_output_tokens(A)
			else:D=min(D,E.max_num_output_tokens(A))
			try:
				B=_A
				for Q in range(L):
					H=[];M=Message(role=_E);M.append_text(J);H.append(M);I=Message(role=_G);I.append_text(G);I.append_image(F);H.append(I);B=E.generate_text(messages=H,model_name=A,temperature=K,max_num_output_tokens=D);B=str(B)
					if not B:aprint(f"Response is empty. Trying again...");continue
					C=B.lower().strip()
					if len(C)<3:aprint(f"Response is empty. Trying again...");continue
					if'sorry'in C and('i cannot'in C or"i can't"in C or'i am unable'in C)or'i cannot assist'in C or"i can't assist"in C or'i am unable to assist'in C or"I'm sorry"in C:aprint(f"Model {A} refuses to assist (response: {B}). Trying again...");continue
				O={_D:A,_H:K,_I:D,_J:L,_E:J,_K:G};E.callback_manager.on_image_description(image_uri=F,description=B,**O)
				with asection(f"Description:"):aprint(B)
				return B
			except Exception as N:aprint(f"Error: '{N}'");import traceback as P;P.print_exc();return f"Error: '{N}'"
	def describe_audio(D,audio_uri,system='You are a helpful AI assistant that can describe and analyse audio.',query='Here is an audio file, please carefully describe its contents in detail. If it is speech, please transcribe completely and accurately.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):
		K=number_of_tries;J=temperature;I=system;F=query;E=audio_uri;B=max_output_tokens;A=model_name
		with asection(f"Asking model {A} to describe a given audio: '{E}':"):
			aprint(f"Query: '{F}'");aprint(f"Model: '{A}'");aprint(f"Max tokens: '{B}'")
			if A is _A:A=D.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Audio])
			if A is _A:raise FeatureNotAvailableError(f"Model '{A}' does not support audio.")
			if B is _A:B=D.max_num_output_tokens(A)
			else:B=min(B,D.max_num_output_tokens(A))
			try:
				for Q in range(K):
					G=[];L=Message(role=_E);L.append_text(I);G.append(L);H=Message(role=_G);H.append_text(F);H.append_audio(E);G.append(H);C=D.generate_text(messages=G,model_name=A,temperature=J,max_num_output_tokens=B);C=str(C)
					if not C:aprint(f"Response is empty. Trying again...");continue
					N=C.lower().strip()
					if len(N)<3:aprint(f"Response is empty. Trying again...");continue
					O={_D:A,_H:J,_I:B,_J:K,_E:I,_K:F};D.callback_manager.on_audio_description(audio_uri=E,description=C,**O)
					with asection(f"Description:"):aprint(C)
					return C
				return
			except Exception as M:aprint(f"Error: '{M}'");import traceback as P;P.print_exc();return f"Error: '{M}'"
	def describe_video(D,video_uri,system='You are a helpful AI assistant that can describe and analyse videos.',query='Here is a video file, please carefully and completely describe it in detail.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):
		K=number_of_tries;J=temperature;I=system;F=query;E=video_uri;B=max_output_tokens;A=model_name
		with asection(f"Asking model {A} to describe a given video: '{E}':"):
			aprint(f"Query: '{F}'");aprint(f"Model: '{A}'");aprint(f"Max tokens: '{B}'")
			if A is _A:A=D.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Video])
			if A is _A:raise FeatureNotAvailableError(f"Model '{A}' does not support video.")
			if B is _A:B=D.max_num_output_tokens(A)
			else:B=min(B,D.max_num_output_tokens(A))
			try:
				for Q in range(K):
					G=[];L=Message(role=_E);L.append_text(I);G.append(L);H=Message(role=_G);H.append_text(F);H.append_video(E);G.append(H);C=D.generate_text(messages=G,model_name=A,temperature=J,max_num_output_tokens=B);C=str(C)
					if not C:aprint(f"Response is empty. Trying again...");continue
					N=C.lower().strip()
					if len(N)<3:aprint(f"Response is empty. Trying again...");continue
					O={_D:A,_H:J,_I:B,_J:K,_E:I,_K:F};D.callback_manager.on_video_description(video_uri=E,description=C,**O)
					with asection(f"Description:"):aprint(C)
					return C
				return
			except Exception as M:aprint(f"Error: '{M}'");import traceback as P;P.print_exc();return f"Error: '{M}'"
	def describe_document(D,document_uri,system='You are a helpful AI assistant that can describe and analyse documents.',query='Here is a document, please carefully and completely describe it in detail.',model_name=_A,temperature=0,max_output_tokens=_A,number_of_tries=4):
		K=number_of_tries;J=temperature;I=system;F=query;E=document_uri;B=max_output_tokens;A=model_name
		with asection(f"Asking model {A} to describe a given document: '{E}':"):
			aprint(f"Query: '{F}'");aprint(f"Model: '{A}'");aprint(f"Max tokens: '{B}'")
			if A is _A:A=D.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Document])
			if A is _A:raise FeatureNotAvailableError(f"Model '{A}' does not support documents.")
			if B is _A:B=D.max_num_output_tokens(A)
			else:B=min(B,D.max_num_output_tokens(A))
			try:
				for Q in range(K):
					G=[];L=Message(role=_E);L.append_text(I);G.append(L);H=Message(role=_G);H.append_text(F);H.append_document(E);G.append(H);C=D.generate_text(messages=G,model_name=A,temperature=J,max_num_output_tokens=B);C=str(C)
					if not C:aprint(f"Response is empty. Trying again...");continue
					N=C.lower().strip()
					if len(N)<3:aprint(f"Response is empty. Trying again...");continue
					O={_D:A,_H:J,_I:B,_J:K,_E:I,_K:F};D.callback_manager.on_document_description(video_uri=E,description=C,**O)
					with asection(f"Description:"):aprint(C)
					return C
				return
			except Exception as M:aprint(f"Error: '{M}'");import traceback as P;P.print_exc();return f"Error: '{M}'"
```

==========
Prog. Lang. Code File: exceptions.py
Size: 138 bytes
==========

exceptions.py
```py
class APIError(Exception):0
class APINotAvailableError(APIError):0
class FeatureNotAvailableError(APIError):0
```

==========
Prog. Lang. Code File: feature_scanner.py
Size: 47.51 kilobytes
==========

feature_scanner.py
```py
_P='metadata'
_O='sentence'
_N='harvard'
_M='noise2self_paper_page4.pdf'
_L='python.png'
_K='conversion'
_J='scan_results'
_I='harvard.wav'
_H='results'
_G=True
_F=.0
_E='user'
_D='You are a helpful assistant.'
_C='system'
_B=None
_A=False
import os,traceback
from datetime import datetime
from typing import Dict,List,Optional,Type,Union
import yaml
from arbol import Arbol,acapture,aprint,asection
from pydantic import BaseModel
from litemind.agent.messages.message import Message
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import BaseApi
from litemind.apis.model_features import ModelFeatures
from litemind.apis.providers.google.utils.is_reasoning import is_gemini_reasoning_model
from litemind.apis.providers.openai.utils.is_reasoning import is_openai_reasoning_model
from litemind.ressources.media_resources import MediaResources
__model_feature_scanner=_B
def get_default_model_feature_scanner():
	global __model_feature_scanner
	if __model_feature_scanner is _B:__model_feature_scanner=ModelFeatureScanner();__model_feature_scanner.load_results(os.path.join(os.path.dirname(__file__),_J))
	return __model_feature_scanner
class ModelFeatureScanner(MediaResources):
	def __init__(A,output_dir=_B,print_exception_stacktraces=_A):A.output_dir=output_dir or os.path.join(os.path.dirname(__file__),_J);A.print_exception_stacktraces=print_exception_stacktraces;os.makedirs(A.output_dir,exist_ok=_G);A.scan_results={}
	@staticmethod
	def _snake_case(name):A='\\1_\\2';import re;B=re.sub('(.)([A-Z][a-z]+)',A,name);return re.sub('([a-z0-9])([A-Z])',A,B).lower()
	@staticmethod
	def _api_class_to_name(api_class):return api_class.__name__
	@staticmethod
	def _api_name_to_class(api_name):
		B=api_name;import sys;G=['litemind.apis.providers','litemind.apis.tests',__name__];D=[];E=[]
		for(H,A)in list(sys.modules.items()):
			if A is _B:continue
			F=getattr(A,'__name__',_B)
			if F is _B:continue
			if any(A in F for A in G):D.append(A)
			else:E.append(A)
		for A in D+E:
			if hasattr(A,B):
				C=getattr(A,B)
				if isinstance(C,type)and issubclass(C,BaseApi):return C
		aprint(f"Warning: API class '{B}' not found in loaded modules or not a subclass of BaseApi.")
	@staticmethod
	def _feature_to_name(feature):return feature.name
	@staticmethod
	def _name_to_feature(feature_name):return ModelFeatures[feature_name]
	def get_supported_features(A,api_class,model_name):
		C=model_name;B=api_class
		if B not in A.scan_results or C not in A.scan_results[B]:return[]
		return[A for(A,B)in A.scan_results[B][C].items()if B]
	def supports_feature(A,api_class,model_name,feature):
		C=model_name;B=api_class
		if B not in A.scan_results or C not in A.scan_results[B]:return _A
		return A.scan_results[B][C].get(feature,_A)
	def supports_any_feature(A,api_class,model_name):
		C=model_name;B=api_class
		if B not in A.scan_results or C not in A.scan_results[B]:return _A
		return any(A.scan_results[B][C].values())
	def scan_apis(C,api_classes,model_names=_B,models_per_api=_B):
		F=model_names;E=api_classes;D=models_per_api
		with asection(f"Scanning APIs: {", ".join([A.__name__ for A in E])}"):
			aprint(f"Scanning {len(E)} APIs...")
			for B in E:
				try:
					G=B(allow_media_conversions=_A)
					if not G.check_availability_and_credentials():aprint(f"API {B.__name__} is not available. Skipping.");continue
					A=G.list_models()
					if F:
						A=list(set(A)&set(F))
						if not A:aprint(f"No matching models found for {B.__name__} with provided names.");continue
						I=set(F)-set(A)
						if I:aprint(f"Warning: The following models were not found in {B.__name__}: {", ".join(I)}")
					if D and len(A)>D:aprint(f"Limiting scan to {D} models for {B.__name__}");A=A[:D]
					aprint(f"Scanning {len(A)} models from {B.__name__}:");C.scan_results[B]={}
					for(J,H)in enumerate(A,start=1):C.scan_results[B][H]=C.scan_model_features(G,H);aprint(f"Done scanning model {J}/{len(A)}: {H}")
				except Exception as K:aprint(f"Error scanning {B.__name__}: {K}");traceback.print_exc()
		return C.scan_results
	def scan_model_features(D,api,model_name):
		F=model_name;B={}
		with asection(f"Scanning model: {F} from API: {api.__class__.__name__}"):
			for C in ModelFeatures:
				A=C.name
				if _K in A.lower():continue
				I=f"test_{D._snake_case(A)}";G=getattr(D,I,_B)
				if G:
					try:
						E=Arbol.enable_output;Arbol.enable_output=_A
						with acapture():H=G(api,F)
						Arbol.enable_output=E;B[C]=H;aprint(f"      {"✅"if H else"❌"} {A}")
					except Exception as J:
						Arbol.enable_output=E;aprint(f"      ❌ {A} (Error: {J})");B[C]=_A
						if D.print_exception_stacktraces:
							aprint('Stack trace:')
							with acapture():traceback.print_exc()
					Arbol.enable_output=E
				else:aprint(f"      ❌ {A} (No test method found!! Check!!)");B[C]=_A
		return B
	def test_feature(H,api_class,model_name,feature,return_error_info=_A,disable_output=_A,allow_media_conversions=_A):
		F=disable_output;E='error';D='result';C=return_error_info;G=feature.name
		if _K in G.lower():A=_A;B='Conversion features are not directly testable';return{D:A,E:B}if C else A
		K=f"test_{H._snake_case(G)}";I=getattr(H,K,_B)
		if I:
			try:
				J=Arbol.enable_output
				if F:Arbol.enable_output=_A
				with acapture():L=api_class(allow_media_conversions=allow_media_conversions);A=I(L,model_name)
				if F:Arbol.enable_output=J
				return{D:A,E:''}if C else A
			except Exception as M:
				if F:Arbol.enable_output=J
				B=str(M);return{D:_A,E:B}if C else _A
		else:B=f"No test method found for feature: {G}";return{D:_A,E:B}if C else _A
	def test_text_generation(E,api,model_name):
		try:
			C=[Message(role=_C,text=_D),Message(role=_E,text="Say 'hello world'")];A=api.generate_text(model_name=model_name,messages=C,temperature=_F)
			if not A or len(A)<1:return _A
			B=str(A[0]).lower();return'hello'in B or'world'in B
		except Exception as D:aprint(f"Error in text generation test: {D}");return _A
	def test_structured_text_generation(G,api,model_name):
		try:
			class C(BaseModel):name:str;age:int
			D=[Message(role=_C,text=_D),Message(role=_E,text='Return information about a person named Bob who is 30 years old.')];A=api.generate_text(model_name=model_name,messages=D,response_format=C,temperature=_F)
			if not A or len(A)<1:return _A
			E=A[0][-1];B=E.get_content();return isinstance(B,C)and hasattr(B,'name')and hasattr(B,'age')
		except Exception as F:aprint(f"Error in test_structured_text_generation: {F}");return _A
	def test_image_generation(D,api,model_name):
		try:A=api.generate_image(positive_prompt='A beautiful sunset over mountains',model_name=model_name);from PIL.Image import Image as B;return A is not _B and isinstance(A,B)
		except Exception as C:aprint(f"Error in test_image_generation: {C}");return _A
	def test_audio_generation(C,api,model_name):
		try:A=api.generate_audio(text='Hello, this is a test.',model_name=model_name);return A is not _B and isinstance(A,str)
		except Exception as B:aprint(f"Error in test_audio_generation: {B}");return _A
	def test_video_generation(C,api,model_name):
		try:A=api.generate_video(description='A short video of a spinning cube',model_name=model_name);return A is not _B and isinstance(A,str)
		except Exception as B:aprint(f"Error in test_video_generation: {B}");return _A
	def test_thinking(F,api,model_name):
		D='thinking';B=model_name;A=api
		try:
			if A.__class__.__name__.lower()=='anthropicapi':C=D in B.lower()
			elif A.__class__.__name__.lower()=='ollamaapi':C=D in B.lower()
			elif A.__class__.__name__.lower()=='geminiapi':C=is_gemini_reasoning_model(B)
			elif A.__class__.__name__.lower()=='openaiapi':C=is_openai_reasoning_model(B)
			return C
		except Exception as E:aprint(f"Error in test_thinking: {E}");return _A
	def test_text_embeddings(D,api,model_name):
		try:B=['Hello, world!'];A=api.embed_texts(texts=B,model_name=model_name,dimensions=512);return A is not _B and len(A)==1 and len(A[0])>0 and isinstance(A[0][0],float)
		except Exception as C:aprint(f"Error in test_text_embeddings: {C}");return _A
	def test_image_embeddings(B,api,model_name):
		try:C=B.get_local_test_image_uri(_L);A=api.embed_images(image_uris=[C],model_name=model_name,dimensions=512);return A is not _B and len(A)==1 and len(A[0])>0 and isinstance(A[0][0],float)
		except Exception as D:aprint(f"Error in test_image_embeddings: {D}");return _A
	def test_audio_embeddings(B,api,model_name):
		try:C=B.get_local_test_audio_uri(_I);A=api.embed_audios(audio_uris=[C],model_name=model_name,dimensions=512);return A is not _B and len(A)==1 and len(A[0])>0 and isinstance(A[0][0],float)
		except Exception as D:aprint(f"Error in test_audio_embeddings: {D}");return _A
	def test_video_embeddings(B,api,model_name):
		try:C=B.get_local_test_video_uri('flying.mp4');A=api.embed_videos(video_uris=[C],model_name=model_name,dimensions=512);return A is not _B and len(A)==1 and len(A[0])>0 and isinstance(A[0][0],float)
		except Exception as D:aprint(f"Error in test_video_embeddings: {D}");return _A
	def test_document_embeddings(B,api,model_name):
		try:C=B.get_local_test_document_uri(_M);A=api.embed_documents(document_uris=[C],model_name=model_name,dimensions=512);return A is not _B and len(A)==1 and len(A[0])>0 and isinstance(A[0][0],float)
		except Exception as D:aprint(f"Error in test_document_embeddings: {D}");return _A
	def test_image(C,api,model_name):
		try:
			D=C.get_local_test_image_uri(_L);B=[Message(role=_C,text=_D),Message(role=_E,text='Describe this image.')];B[1].append_image(D);A=api.generate_text(model_name=model_name,messages=B,temperature=_F)
			if not A or len(A)<1:return _A
			E=str(A[0]).lower();F=['python','logo','snake','blue','yellow'];return any(A in E for A in F)
		except Exception as G:aprint(f"Error in test_image: {G}");return _A
	def test_audio(C,api,model_name):
		try:
			D=C.get_local_test_audio_uri(_I);B=[Message(role=_C,text=_D),Message(role=_E,text='What is in this audio file?')];B[1].append_audio(D);A=api.generate_text(model_name=model_name,messages=B,temperature=_F)
			if not A or len(A)<1:return _A
			E=str(A[0]).lower();F=[_N,_O,'ham','smell','beer'];return any(A in E for A in F)
		except Exception as G:aprint(f"Error in test_audio: {G}");return _A
	def test_video(C,api,model_name):
		try:
			D=C.get_local_test_video_uri('video.mp4');B=[Message(role=_C,text=_D),Message(role=_E,text="Describe what's happening in this video.")];B[1].append_video(D);A=api.generate_text(model_name=model_name,messages=B,temperature=_F)
			if not A or len(A)<1:return _A
			if'm unable to'in str(A[0]).lower():return _A
			E=str(A[0]).lower();F=['fly ','flying','saucer','hover','disc ','circular','aircraft','vehicle','testing','drone','facility']
			for G in F:
				if G in E:return _G
			return _A
		except Exception as H:aprint(f"Error in test_video: {H}");return _A
	def test_document(C,api,model_name):
		try:
			D=C.get_local_test_document_uri(_M);B=[Message(role=_C,text=_D),Message(role=_E,text='What is this document about?')];B[1].append_document(D);A=api.generate_text(model_name=model_name,messages=B,temperature=_F)
			if not A or len(A)<1:return _A
			E=str(A[0]).lower();F=['noise','noise2self','j-invariance','denoising','paper'];return any(A in E for A in F)
		except Exception as G:aprint(f"Error in test_document: {G}");return _A
	def test_tools(G,api,model_name):
		try:
			def C(first_name):return'Gerrard'
			B=ToolSet();B.add_function_tool(C,'Get the last name for the first name.');D=[Message(role=_C,text=_D),Message(role=_E,text="What is Paul's last name?")];A=api.generate_text(model_name=model_name,messages=D,toolset=B,temperature=_F)
			if A is _B or len(A)<1:return _A
			E=str(A[-1]).lower();return'gerrard'in E
		except Exception as F:aprint(f"Error in test_tools: {F}");return _A
	def test_web_search_tool(F,api,model_name):
		try:
			B=ToolSet();B.add_builtin_web_search_tool();D=[Message(role=_C,text=_D),Message(role=_E,text='What is the weather in London now?')];A=api.generate_text(model_name=model_name,messages=D,toolset=B,temperature=_F)
			if A is _B or len(A)<1:return _A
			C=str(A[-1]).lower();return any(A in C for A in'0123456789')or'london'in C
		except Exception as E:aprint(f"Error in test_web_search_tool: {E}");return _A
	def test_mcp_tool(F,api,model_name):
		try:
			C=ToolSet();C.add_builtin_mcp_tool(server_name='deepwiki',server_url='https://mcp.deepwiki.com/mcp',allowed_tools=['ask_question']);D=[Message(role=_C,text=_D),Message(role=_E,text='What transport protocols does the 2025-03-26 version of the MCP spec (modelcontextprotocol/modelcontextprotocol) support?')];A=api.generate_text(model_name=model_name,messages=D,toolset=C,temperature=_F)
			if A is _B or len(A)<1:return _A
			B=str(A[-1]).lower();return'stdio'in B or'streamable'in B or'http'in B
		except Exception as E:aprint(f"Error in test_mcp_tool: {E}");return _A
	def test_audio_transcription(B,api,model_name):
		try:
			C=B.get_local_test_audio_uri(_I);A=api.transcribe_audio(audio_uri=C,model_name=model_name)
			if not A or not isinstance(A,str):return _A
			A=A.lower();D=[_N,_O,'ham'];return any(B in A for B in D)
		except Exception as E:aprint(f"Error in test_audio_transcription: {E}");return _A
	def save_results(A,folder=_B):
		H='%Y%m%d_%H%M%S';B=folder or A.output_dir;os.makedirs(B,exist_ok=_G);C=[];D=datetime.now().strftime(H)
		for(I,J)in A.scan_results.items():
			E=A._api_class_to_name(I);K=f"model_features_{E}_{D}.scan.yaml";F=os.path.join(B,K);L={B:{A._feature_to_name(B):C for(B,C)in C.items()}for(B,C)in J.items()};M={_P:{'timestamp':datetime.now().isoformat(),'scan_version':'1.0','api':E},_H:L}
			with open(F,'w')as N:yaml.dump(M,N,default_flow_style=_A)
			aprint(f"Results for {E} saved to {F}");C.append(F)
		if A.scan_results:
			D=datetime.now().strftime(H);O=A.generate_markdown_report();G=os.path.join(B,f"report_{D}.scan.md")
			with open(G,'w')as P:P.write(O)
			aprint(f"Comprehensive Markdown report saved to {G}");C.append(G)
		return C
	def load_results(C,folder):
		O='.yaml';B=folder;L=0
		if not os.path.exists(B)or not os.path.isdir(B):aprint(f"Warning: Load folder '{B}' does not exist or is not a directory.");return C.scan_results
		M=os.listdir(B)
		if not M:aprint(f"Warning: Load folder '{B}' is empty. No results to load.");return C.scan_results
		M.sort(key=lambda x:os.path.getmtime(os.path.join(B,x)),reverse=_G)
		for D in os.listdir(B):
			if D.endswith(O):
				A=os.path.join(B,D)
				try:
					with open(A,'r')as P:E=yaml.safe_load(P)
					if not isinstance(E,dict):aprint(f"Warning: Skipping file {A} due to invalid YAML structure (not a dict).");continue
					Q=E.get(_P,{}).get('api');F=Q or(D.split('_')[3]if len(D.split('_'))>3 else D.replace('model_feature_scan_','').replace(O,''));G=C._api_name_to_class(F)
					if G is _B:aprint(f"Warning: Skipping results from file {A} as API class '{F}' could not be resolved to a BaseApi subclass.");continue
					I={}
					if _H in E and isinstance(E[_H],dict):
						for(J,K)in E[_H].items():
							if isinstance(K,dict):
								I[J]={C._name_to_feature(A):B for(A,B)in K.items()if A in ModelFeatures.__members__};N=[A for A in K if A not in ModelFeatures.__members__]
								if N:aprint(f"Warning: Unrecognized feature names {N} for model '{J}' in {A}. These were skipped.")
							else:aprint(f"Warning: Invalid features format for model '{J}' in {A}. Skipping model.")
					else:aprint(f"Warning: No valid 'results' data found in {A} for API '{F}'. Skipping file.");continue
					if len(I)==0:aprint(f"Warning: No models or features successfully parsed from {A} for API '{F}'. Skipping file.");continue
					if G in C.scan_results:C.scan_results[G].update(I)
					else:C.scan_results[G]=I
					aprint(f"Results for {F} (class: {G.__name__}) loaded from {A}");L+=1
				except yaml.YAMLError as H:aprint(f"Error parsing YAML file {A}: {H}")
				except KeyError as H:aprint(f"Error processing feature name in file {A}: Invalid feature key {H}. This file might be corrupted or have an old/invalid feature name.")
				except Exception as H:aprint(f"Error processing file {A}: {H}");traceback.print_exc()
		if L==0:aprint(f"No YAML results successfully loaded from {B}.")
		return C.scan_results
	def generate_markdown_report(D):
		A=[];B=list(ModelFeatures);A.append('# Model Feature Scan Report');A.append(f"_Report generated on: {datetime.now().isoformat()}_");A.append('')
		if not D.scan_results:A.append('No scan results available. Please run `scan_apis()` or `load_results()` first.');return'\n'.join(A)
		H=sorted(D.scan_results.items(),key=lambda item:D._api_class_to_name(item[0]))
		for(O,(P,E))in enumerate(H):
			Q=D._api_class_to_name(P);A.append(f"## API: {Q}");A.append('\n### API Summary');C=len(E);A.append(f"*   **Total Models Scanned:** {C}")
			if C>0:
				I=0;J={A:0 for A in B}
				for(G,K)in E.items():
					R=sum(1 for A in B if K.get(A,_A));I+=R
					for F in B:
						if K.get(F,_A):J[F]+=1
				S=I/C;A.append(f"*   **Average Supported Features per Model:** {S:.2f}");A.append('\n#### Feature Support Across Models:');T=sorted(J.items(),key=lambda item:item[0].name)
				for(F,L)in T:U=L/C*100;A.append(f"*   {F.name}: {U:.1f}% ({L}/{C})")
			else:A.append('*   No models scanned for this API.')
			A.append('\n### Model Details')
			if C>0:
				V=sorted(E.keys())
				for G in V:
					M=E[G];W=sum(1 for A in B if M.get(A,_A));A.append(f"\n#### Model: {G} ({W}/{len(B)} features supported)")
					for N in B:X=M.get(N,_A);Y='✅'if X else'❌';A.append(f"*   {Y} {N.name}")
			else:A.append('_No model details to display as no models were scanned for this API._')
			if O<len(H)-1:A.append('\n---')
			A.append('')
		A.append('_This report is auto-generated by Litemind. Some features may not be supported due to model limitations, API restrictions, or potential bugs in Litemind._');return'\n'.join(A)
```

==========
Prog. Lang. Code File: model_features.py
Size: 8.06 kilobytes
==========

model_features.py
```py
from enum import Enum
from typing import List,Optional,Set,Type,Union
class ModelFeatures(Enum):
	TextGeneration='TextGeneration';StructuredTextGeneration='StructuredTextGeneration';ImageGeneration='ImageGeneration';AudioGeneration='AudioGeneration';VideoGeneration='VideoGeneration';Thinking='Thinking';TextEmbeddings='TextEmbeddings';ImageEmbeddings='ImageEmbeddings';AudioEmbeddings='AudioEmbeddings';VideoEmbeddings='VideoEmbeddings';DocumentEmbeddings='DocumentEmbeddings';Image='Image';Audio='Audio';Video='Video';Document='Documents';Tools='Tools';WebSearchTool='WebSearchTool';MCPTool='MCPTool';AudioTranscription='AudioTranscription';ImageConversion='ImageConversion';AudioConversion='AudioConversion';VideoConversion='VideoConversion';DocumentConversion='DocumentConversion'
	@staticmethod
	def normalise(features):
		A=features
		if A is None:return
		if isinstance(A,str):A=[A]
		if isinstance(A,ModelFeatures):A=[A]
		if all(isinstance(A,ModelFeatures)for A in A):return A
		if all(isinstance(A,str)for A in A):A=[A.lower()for A in A]
		else:A=[str(A)for A in A]
		B=[]
		for C in A:
			D=False
			for E in ModelFeatures:
				F=E.name.lower()
				if F==C:B.append(E);D=True;break
			if not D:raise ValueError(f"Unknown feature: {C} should be one of {", ".join([A.name for A in ModelFeatures])}")
		return B
	@staticmethod
	def get_supported_media_types(features):
		A=features;A=ModelFeatures.normalise(A);from litemind.media.media_base import MediaBase;from litemind.media.types.media_audio import Audio;from litemind.media.types.media_document import Document as C;from litemind.media.types.media_image import Image;from litemind.media.types.media_text import Text;from litemind.media.types.media_video import Video;B=set()
		if ModelFeatures.TextGeneration in A:B.add(Text)
		if ModelFeatures.Image in A:B.add(Image)
		if ModelFeatures.Audio in A:B.add(Audio)
		if ModelFeatures.Video in A:B.add(Video)
		if ModelFeatures.Document in A:B.add(C)
		return B
	@staticmethod
	def get_features_needed_for_media_types(media_types):
		A=set()
		for B in media_types:
			from litemind.media.types.media_audio import Audio;from litemind.media.types.media_document import Document as C;from litemind.media.types.media_image import Image;from litemind.media.types.media_text import Text;from litemind.media.types.media_video import Video
			if issubclass(B,Text):A.add(ModelFeatures.TextGeneration)
			elif issubclass(B,Image):A.add(ModelFeatures.Image)
			elif issubclass(B,Audio):A.add(ModelFeatures.Audio)
			elif issubclass(B,Video):A.add(ModelFeatures.Video)
			elif issubclass(B,C):A.add(ModelFeatures.Document)
		return A
	def __str__(A):return A.name
	def __repr__(A):return A.name
```




==========
Prog. Lang. Code File: base_callbacks.py
Size: 7.71 kilobytes
==========

base_callbacks.py
```py
from abc import ABC
from typing import Any,List,Sequence
from litemind.agent.messages.message import Message
class BaseCallbacks(ABC):
	def on_availability_check(A,available):0
	def on_model_list(A,models,**B):0
	def on_best_model_selected(A,model_name,**B):0
	def on_text_generation(A,messages,response,**B):0
	def on_text_streaming(A,fragment,**B):0
	def on_audio_transcription(A,transcription,audio_uri,**B):0
	def on_document_conversion(A,document_uri,markdown,**B):0
	def on_video_conversion(A,video_uri,images,audio,**B):0
	def on_audio_generation(A,text,audio_uri,**B):0
	def on_image_generation(A,prompt,image,**B):0
	def on_text_embedding(A,texts,embeddings,**B):0
	def on_image_embedding(A,image_uris,embeddings,**B):0
	def on_audio_embedding(A,audio_uris,embeddings,**B):0
	def on_video_embedding(A,video_uris,embeddings,**B):0
	def on_document_embedding(A,document_uris,embeddings,**B):0
	def on_image_description(A,image_uri,description,**B):0
	def on_audio_description(A,audio_uri,description,**B):0
	def on_video_description(A,video_uri,description,**B):0
	def on_document_description(A,document_uri,description,**B):0
```

==========
Prog. Lang. Code File: callback_manager.py
Size: 5.38 kilobytes
==========

callback_manager.py
```py
from typing import Any,List,Sequence,Union
from litemind.agent.messages.message import Message
from litemind.apis.callbacks.base_callbacks import BaseCallbacks
class CallbackManager(BaseCallbacks):
	def __init__(A):A.callbacks=[]
	def add_callback(A,callback):
		B=callback
		if B not in A.callbacks:A.callbacks.append(B)
	def add_callbacks(B,callbacks_):
		A=callbacks_
		if isinstance(A,CallbackManager):B.callbacks.extend(A.callbacks)
		elif isinstance(A,Sequence):
			for C in A:
				if isinstance(C,BaseCallbacks):B.callbacks.append(C)
	def remove_callback(A,callback):
		B=callback
		if B in A.callbacks:A.callbacks.remove(B)
	def __contains__(A,callback):return callback in A.callbacks
	def __len__(A):return len(A.callbacks)
	def __getitem__(A,index):return A.callbacks[index]
	def __setitem__(A,index,callback):A.callbacks[index]=callback
	def __delitem__(A,index):del A.callbacks[index]
	def __iter__(A):return iter(A.callbacks)
	def on_availability_check(A,available):
		for B in A.callbacks:B.on_availability_check(available)
	def on_model_list(A,models,**B):
		for C in A.callbacks:C.on_model_list(models,**B)
	def on_best_model_selected(A,model_name,**B):
		for C in A.callbacks:C.on_best_model_selected(model_name,**B)
	def on_text_generation(A,messages,response,**B):
		for C in A.callbacks:C.on_text_generation(messages,response,**B)
	def on_text_streaming(A,fragment,**B):
		for C in A.callbacks:C.on_text_streaming(fragment,**B)
	def on_audio_transcription(A,transcription,audio_uri,**B):
		for C in A.callbacks:C.on_audio_transcription(transcription,audio_uri,**B)
	def on_document_conversion(A,document_uri,markdown,**B):
		for C in A.callbacks:C.on_document_conversion(document_uri,markdown,**B)
	def on_video_conversion(A,video_uri,images,audio,**B):
		for C in A.callbacks:C.on_video_conversion(video_uri,images,audio,**B)
	def on_audio_generation(A,text,audio_uri,**B):
		for C in A.callbacks:C.on_audio_generation(text,audio_uri,**B)
	def on_image_generation(A,prompt,image,**B):
		for C in A.callbacks:C.on_image_generation(prompt,image,**B)
	def on_text_embedding(A,texts,embeddings,**B):
		for C in A.callbacks:C.on_text_embedding(texts,embeddings,**B)
	def on_image_embedding(A,image_uris,embeddings,**B):
		for C in A.callbacks:C.on_image_embedding(image_uris,embeddings,**B)
	def on_audio_embedding(A,audio_uris,embeddings,**B):
		for C in A.callbacks:C.on_audio_embedding(audio_uris,embeddings,**B)
	def on_video_embedding(A,video_uris,embeddings,**B):
		for C in A.callbacks:C.on_video_embedding(video_uris,embeddings,**B)
	def on_document_embedding(A,document_uris,embeddings,**B):
		for C in A.callbacks:C.on_document_embedding(document_uris,embeddings,**B)
	def on_image_description(A,image_uri,description,**B):
		for C in A.callbacks:C.on_image_description(image_uri,description,**B)
	def on_audio_description(A,audio_uri,description,**B):
		for C in A.callbacks:C.on_audio_description(audio_uri,description,**B)
	def on_video_description(A,video_uri,description,**B):
		for C in A.callbacks:C.on_video_description(video_uri,description,**B)
	def on_document_description(A,video_uri,description,**B):
		for C in A.callbacks:C.on_document_description(video_uri,description,**B)
```

==========
Prog. Lang. Code File: print_callbacks.py
Size: 7.94 kilobytes
==========

print_callbacks.py
```py
_A=False
from pprint import pformat
from typing import Any,List,Sequence
from arbol import aprint
from litemind.agent.messages.message import Message
from litemind.apis.callbacks.base_callbacks import BaseCallbacks
class PrintCallbacks(BaseCallbacks):
	def __init__(A,print_model_list=_A,print_best_model_selected=_A,print_text_generation=True,print_text_streaming=_A,print_audio_transcription=_A,print_document_conversion=_A,print_video_conversion=_A,print_audio_generation=True,print_image_generation=True,print_text_embedding=_A,print_image_embedding=_A,print_audio_embedding=_A,print_video_embedding=_A,print_document_embedding=_A,print_image_description=_A,print_audio_description=_A,print_video_description=_A,print_document_description=_A,**B):
		super().__init__();A.print_model_list=print_model_list;A.print_best_model_selected=print_best_model_selected;A.print_text_generation=print_text_generation;A.print_text_streaming=print_text_streaming;A.print_audio_transcription=print_audio_transcription;A.print_document_conversion=print_document_conversion;A.print_video_conversion=print_video_conversion;A.print_audio_generation=print_audio_generation;A.print_image_generation=print_image_generation;A.print_text_embedding=print_text_embedding;A.print_image_embedding=print_image_embedding;A.print_audio_embedding=print_audio_embedding;A.print_video_embedding=print_video_embedding;A.print_document_embedding=print_document_embedding;A.print_image_description=print_image_description;A.print_audio_description=print_audio_description;A.print_video_description=print_video_description;A.print_document_description=print_document_description
		if B:raise ValueError(f"Unknown arguments: {pformat(B)}")
	def on_availability_check(A,available):aprint(f"Availability Check: {available}")
	def on_model_list(B,models,**A):
		if B.print_model_list:
			aprint(f"Model List: {models}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_best_model_selected(B,model_name,**A):
		if B.print_best_model_selected:
			aprint(f"Best Model Selected: {model_name}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_text_generation(B,messages,response,**A):
		if B.print_text_generation:
			aprint(f"Text Generation: Messages: {messages}, Response: {response}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_text_streaming(B,fragment,**A):
		if B.print_text_streaming:
			aprint(f"Text Streaming: {fragment}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_audio_transcription(B,transcription,audio_uri,**A):
		if B.print_audio_transcription:
			aprint(f"Audio Transcription: {transcription}, Audio URI: {audio_uri}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_document_conversion(B,document_uri,markdown,**A):
		if B.print_document_conversion:
			aprint(f"Document Conversion: Document URI: {document_uri}, Markdown: {markdown}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_video_conversion(B,video_uri,images,audio,**A):
		if B.print_video_conversion:
			aprint(f"Video Conversion: Video URI: {video_uri}, Images: {images}, Audio: {audio}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_audio_generation(B,text,audio_uri,**A):
		if B.print_audio_generation:
			aprint(f"Audio Generation: Text: {text}, Audio URI: {audio_uri}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_image_generation(B,prompt,image,**A):
		if B.print_image_generation:
			aprint(f"Image Generation: Prompt: {prompt}, Image: {image}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_text_embedding(B,texts,embeddings,**A):
		if B.print_text_embedding:
			aprint(f"Text Embedding: Texts: {texts}, Embeddings: {embeddings}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_image_embedding(B,image_uris,embeddings,**A):
		if B.print_image_embedding:
			aprint(f"Image Embedding: Image URIs: {image_uris}, Embeddings: {embeddings}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_audio_embedding(B,audio_uris,embeddings,**A):
		if B.print_audio_embedding:
			aprint(f"Audio Embedding: Audio URIs: {audio_uris}, Embeddings: {embeddings}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_video_embedding(B,video_uris,embeddings,**A):
		if B.print_video_embedding:
			aprint(f"Video Embedding: Video URIs: {video_uris}, Embeddings: {embeddings}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_document_embedding(B,document_uris,embeddings,**A):
		if B.print_document_embedding:
			aprint(f"Document Embedding: Document URIs: {document_uris}, Embeddings: {embeddings}")
			if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_image_description(B,image_uri,description,**A):
		aprint(f"Image Description: Image URI: {image_uri}, Description: {description}")
		if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_audio_description(B,audio_uri,description,**A):
		aprint(f"Audio Description: Audio URI: {audio_uri}, Description: {description}")
		if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_video_description(B,video_uri,description,**A):
		aprint(f"Video Description: Video URI: {video_uri}, Description: {description}")
		if A:aprint(f"Additional arguments: {pformat(A)}")
	def on_document_description(B,document_uri,description,**A):
		aprint(f"Document Description: Document URI: {document_uri}, Description: {description}")
		if A:aprint(f"Additional arguments: {pformat(A)}")
```




==========
Prog. Lang. Code File: test_print_callbacks.py
Size: 7.59 kilobytes
==========

test_print_callbacks.py
```py
_G='video_uri'
_F='document_uri'
_E='audio_uri'
_D='description'
_C="Additional arguments: {'param': 'value'}"
_B='value'
_A=True
import pytest
from litemind.agent.messages.message import Message
from litemind.apis.callbacks.print_callbacks import PrintCallbacks
@pytest.fixture
def print_callbacks():return PrintCallbacks(print_text_embedding=_A,print_audio_embedding=_A,print_video_embedding=_A,print_image_embedding=_A,print_document_embedding=_A,print_audio_description=_A,print_image_description=_A,print_audio_generation=_A,print_video_description=_A,print_document_description=_A,print_image_generation=_A,print_video_conversion=_A,print_document_conversion=_A,print_audio_transcription=_A,print_text_streaming=_A,print_text_generation=_A,print_best_model_selected=_A,print_model_list=_A)
def test_on_availability_check(print_callbacks,capsys):print_callbacks.on_availability_check(_A);A=capsys.readouterr();assert'Availability Check: True'in A.out
def test_on_model_list(print_callbacks,capsys):print_callbacks.on_model_list(['model1','model2'],param=_B);A=capsys.readouterr();assert"Model List: ['model1', 'model2']"in A.out;assert _C in A.out
def test_on_best_model_selected(print_callbacks,capsys):print_callbacks.on_best_model_selected('best_model',param=_B);A=capsys.readouterr();assert'Best Model Selected: best_model'in A.out;assert _C in A.out
def test_on_text_generation(print_callbacks,capsys):B='user';C=[Message(role=B,text='Hello'),Message(role=B,text='World')];D=Message('Response');print_callbacks.on_text_generation(C,D,param=_B);A=capsys.readouterr();assert'Text Generation: Messages: [*user*:'in A.out;assert _C in A.out
def test_on_text_streaming(print_callbacks,capsys):print_callbacks.on_text_streaming('fragment',param=_B);A=capsys.readouterr();assert'Text Streaming: fragment'in A.out;assert _C in A.out
def test_on_audio_transcription(print_callbacks,capsys):print_callbacks.on_audio_transcription('transcription',_E,param=_B);A=capsys.readouterr();assert'Audio Transcription: transcription, Audio URI: audio_uri'in A.out;assert _C in A.out
def test_on_document_conversion(print_callbacks,capsys):print_callbacks.on_document_conversion(_F,'markdown',param=_B);A=capsys.readouterr();assert'Document Conversion: Document URI: document_uri, Markdown: markdown'in A.out;assert _C in A.out
def test_on_video_conversion(print_callbacks,capsys):print_callbacks.on_video_conversion(_G,['image1','image2'],'audio',param=_B);A=capsys.readouterr();assert"Video Conversion: Video URI: video_uri, Images: ['image1', 'image2'], Audio: audio"in A.out;assert _C in A.out
def test_on_audio_generation(print_callbacks,capsys):print_callbacks.on_audio_generation('text',_E,param=_B);A=capsys.readouterr();assert'Audio Generation: Text: text, Audio URI: audio_uri'in A.out;assert _C in A.out
def test_on_image_generation(print_callbacks,capsys):print_callbacks.on_image_generation('prompt','image',param=_B);A=capsys.readouterr();assert'Image Generation: Prompt: prompt, Image: image'in A.out;assert _C in A.out
def test_on_text_embedding(print_callbacks,capsys):print_callbacks.on_text_embedding(['text1','text2'],[[.1,.2],[.3,.4]],param=_B);A=capsys.readouterr();assert"Text Embedding: Texts: ['text1', 'text2'], Embeddings: [[0.1, 0.2], [0.3, 0.4]]"in A.out;assert _C in A.out
def test_on_image_embedding(print_callbacks,capsys):print_callbacks.on_image_embedding(['image_uri1','image_uri2'],[[.1,.2],[.3,.4]],param=_B);A=capsys.readouterr();assert"Image Embedding: Image URIs: ['image_uri1', 'image_uri2'], Embeddings: [[0.1, 0.2], [0.3, 0.4]]"in A.out;assert _C in A.out
def test_on_audio_embedding(print_callbacks,capsys):print_callbacks.on_audio_embedding(['audio_uri1','audio_uri2'],[[.1,.2],[.3,.4]],param=_B);A=capsys.readouterr();assert"Audio Embedding: Audio URIs: ['audio_uri1', 'audio_uri2'], Embeddings: [[0.1, 0.2], [0.3, 0.4]]"in A.out;assert _C in A.out
def test_on_video_embedding(print_callbacks,capsys):print_callbacks.on_video_embedding(['video_uri1','video_uri2'],[[.1,.2],[.3,.4]],param=_B);A=capsys.readouterr();assert"Video Embedding: Video URIs: ['video_uri1', 'video_uri2'], Embeddings: [[0.1, 0.2], [0.3, 0.4]]"in A.out;assert _C in A.out
def test_on_document_embedding(print_callbacks,capsys):print_callbacks.on_document_embedding(['document_uri1','document_uri2'],[[.1,.2],[.3,.4]],param=_B);A=capsys.readouterr();assert"Document Embedding: Document URIs: ['document_uri1', 'document_uri2'], Embeddings: [[0.1, 0.2], [0.3, 0.4]]"in A.out;assert _C in A.out
def test_on_image_description(print_callbacks,capsys):print_callbacks.on_image_description('image_uri',_D,param=_B);A=capsys.readouterr();assert'Image Description: Image URI: image_uri, Description: description'in A.out;assert _C in A.out
def test_on_audio_description(print_callbacks,capsys):print_callbacks.on_audio_description(_E,_D,param=_B);A=capsys.readouterr();assert'Audio Description: Audio URI: audio_uri, Description: description'in A.out;assert _C in A.out
def test_on_video_description(print_callbacks,capsys):print_callbacks.on_video_description(_G,_D,param=_B);A=capsys.readouterr();assert'Video Description: Video URI: video_uri, Description: description'in A.out;assert _C in A.out
def test_on_document_description(print_callbacks,capsys):print_callbacks.on_document_description(_F,_D,param=_B);A=capsys.readouterr();assert'Document Description: Document URI: document_uri, Description: description'in A.out;assert _C in A.out
```







==========
Prog. Lang. Code File: anthropic_api.py
Size: 26.02 kilobytes
==========

anthropic_api.py
```py
_R='instant-1'
_Q='claude-2'
_P='claude-2.1'
_O='sonnet-4'
_N='opus-4'
_M='claude-opus-4'
_L='claude-sonnet-4'
_K='claude-3'
_J='thinking'
_I='anthropic-beta'
_H='name'
_G='claude-2.0'
_F='enabled'
_E='claude-4'
_D='type'
_C=False
_B=True
_A=None
import os
from typing import List,Optional,Sequence,Type,Union
from pydantic import BaseModel
from litemind.agent.messages.message import Message
from litemind.agent.messages.message_block import MessageBlock
from litemind.agent.tools.base_tool import BaseTool
from litemind.agent.tools.builtin_tools.mcp_tool import BuiltinMCPTool
from litemind.agent.tools.builtin_tools.web_search_tool import BuiltinWebSearchTool
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import ModelFeatures
from litemind.apis.callbacks.callback_manager import CallbackManager
from litemind.apis.default_api import DefaultApi
from litemind.apis.exceptions import APIError,APINotAvailableError
from litemind.apis.feature_scanner import get_default_model_feature_scanner
from litemind.apis.providers.anthropic.utils.check_availability import check_anthropic_api_availability
from litemind.apis.providers.anthropic.utils.convert_messages import convert_messages_for_anthropic
from litemind.apis.providers.anthropic.utils.format_tools import format_tools_for_anthropic
from litemind.apis.providers.anthropic.utils.list_models import _get_anthropic_models_list
from litemind.apis.providers.anthropic.utils.process_response import process_response_from_anthropic
from litemind.media.media_base import MediaBase
from litemind.media.types.media_action import Action
from litemind.media.types.media_text import Text
_CTX_200K=200000
_CTX_100K=100000
_OUT_64K=64000
_OUT_32K=32000
_OUT_8K=8192
_OUT_4K=4096
_OUT_128K=128000
class AnthropicApi(DefaultApi):
	def __init__(A,api_key=_A,base_url=_A,allow_media_conversions=_B,allow_media_conversions_with_models=_B,callback_manager=_A,**C):
		B=api_key;super().__init__(allow_media_conversions=allow_media_conversions,allow_media_conversions_with_models=allow_media_conversions_with_models,callback_manager=callback_manager)
		try:import anthropic
		except ImportError:raise ImportError('Please install anthropic to use AnthropicApi: pip install anthropic')
		A.feature_scanner=get_default_model_feature_scanner()
		if B is _A:B=os.environ.get('ANTHROPIC_API_KEY')
		if not B:raise APIError('An Anthropic API key is required. Set ANTHROPIC_API_KEY or pass `api_key=...` explicitly.')
		A.api_key=B;A.base_url=base_url;A.anthropic_api_kwargs=C
		try:A.feature_scanner=get_default_model_feature_scanner();from anthropic import Anthropic as D;A.client=D(api_key=A.api_key,base_url=A.base_url,**C);A._model_list=_get_anthropic_models_list(A.client)
		except Exception as E:import traceback as F;F.print_exc();raise APINotAvailableError(f"Error initializing Anthropic client: {E}")
	def check_availability_and_credentials(A,api_key=_A):
		C=api_key
		if C is not _A:from anthropic import Anthropic as F;D=F(api_key=C,base_url=A.base_url,**A.anthropic_api_kwargs)
		else:D=A.client
		B=list(A._model_list);B=[A for A in B if _J not in A];G=B[0];E=check_anthropic_api_availability(D,G);A.callback_manager.on_availability_check(E);return E
	def list_models(C,features=_A,non_features=_A,media_types=_A):
		D=non_features;B=features
		try:
			B=ModelFeatures.normalise(B);D=ModelFeatures.normalise(D);A=list(C._model_list);A+=super().list_models()
			if B:A=C._filter_models(A,features=B,non_features=D,media_types=media_types)
			C.callback_manager.on_model_list(A);return A
		except Exception:raise APIError('Error fetching model list from Anthropic.')
	def get_best_model(B,features=_A,non_features=_A,media_types=_A,exclusion_filters=_A):
		D=non_features;C=features;C=ModelFeatures.normalise(C);D=ModelFeatures.normalise(D);A=B.list_models();A=B._filter_models(A,features=C,non_features=D,media_types=media_types,exclusion_filters=exclusion_filters)
		if A:E=A[0]
		else:E=_A
		B.callback_manager.on_best_model_selected(E);return E
	def has_model_support_for(C,features,media_types=_A,model_name=_A):
		D=media_types;B=model_name;A=features;A=ModelFeatures.normalise(A)
		if B is _A:B=C.get_best_model(features=A,media_types=D)
		if B is _A:return _C
		if super().has_model_support_for(features=A,media_types=D,model_name=B):return _B
		for E in A:
			if not C.feature_scanner.supports_feature(C.__class__,B,E):return _C
		return _B
	def _has_cache_support(B,model_name):
		A=model_name
		if'sonnet'in A or _G in A:return _C
		return _B
	def _has_web_search_support(A,model_name):
		if any(A in model_name.lower()for A in[_E,_K,'claude-sonnet','claude-opus']):return _B
		return _C
	def _has_mcp_support(A,model_name):
		if any(A in model_name.lower()for A in[_E,_L,_M]):return _B
		return _C
	def _has_code_execution_support(A,model_name):
		if any(A in model_name.lower()for A in[_E,_L,_M]):return _B
		return _C
	def _has_thinking_support(A,model_name):
		if any(A in model_name.lower()for A in[_E,'claude-3-7','claude-sonnet-3-7']):return _B
		return _C
	def max_num_input_tokens(C,model_name=_A):
		B=model_name
		if B is _A:B=C.get_best_model()
		A=B.lower()
		if _N in A or _O in A:return _CTX_200K
		if _K in A:return _CTX_200K
		if _P in A:return _CTX_200K
		if _G in A or _Q in A or _R in A:return _CTX_100K
		return _CTX_100K
	def max_num_output_tokens(C,model_name=_A):
		B=model_name
		if B is _A:B=C.get_best_model()
		A=B.lower()
		if _N in A:return _OUT_32K
		if _O in A:return _OUT_64K
		if'3-7-sonnet'in A:return _OUT_64K
		if'3-5-sonnet'in A or'3-5-haiku'in A:return _OUT_8K
		if'3-opus'in A or'3-sonnet'in A or'3-haiku'in A:return _OUT_4K
		if _P in A:return _OUT_4K
		if _G in A or _Q in A or _R in A:return _OUT_4K
		return _OUT_4K
	def _format_tools_and_mcp_for_anthropic(P,toolset):
		L='authorization_token';K='url';E=toolset;from anthropic import NotGiven as M;B=[];F=[];G={}
		if E:
			H=format_tools_for_anthropic(E)
			if H:B=H
			for C in E.list_builtin_tools():
				if isinstance(C,BuiltinWebSearchTool):N=C;O=N.max_web_searches;B.append({_D:'web_search_20250305',_H:'web_search','max_uses':O})
				elif isinstance(C,BuiltinMCPTool):
					A=C;D={_D:K,K:A.server_url,_H:A.server_name}
					if A.headers:
						for(I,J)in A.headers.items():
							if I==L:D[L]=J
							else:D[I]=J
					if A.allowed_tools:D['tool_configuration']={_F:_B,'allowed_tools':A.allowed_tools}
					F.append(D)
				else:raise ValueError(f"Unknown built-in tool: {C.name}")
			if F:G[_I]='mcp-client-2025-04-04'
		if not B:B=M()
		return B,F,G
	def generate_text(B,messages,model_name=_A,temperature=.0,max_num_output_tokens=_A,toolset=_A,use_tools=_B,response_format=_A,**L):
		j='temperature';i='-thinking-low';h='-thinking-mid';g='-thinking-high';f='system';V=1.;U='budget_tokens';O=use_tools;H=response_format;G=temperature;F=messages;D=toolset;C=max_num_output_tokens;A=model_name;k=_C;l=_B;super().generate_text(messages=F,model_name=A,temperature=G,max_num_output_tokens=C,toolset=D,use_tools=O,response_format=H,**L);from anthropic import NotGiven as P
		if A is _A:A=B._get_best_model_for_text_generation(F,D if O else _A,H)
		W='';X=[]
		for Q in F:
			if Q.role==f:
				for I in Q.blocks:
					if I.has_type(Text):m=I.media;W+=m.text
					else:raise ValueError('System message should only contain text blocks.')
			else:X.append(Q)
		R=B._preprocess_messages(messages=X,allowed_media_types=B._get_allowed_media_types_for_text_generation(model_name=A),exclude_extensions=['pdf'])
		if C is _A:C=B.max_num_output_tokens(A)
		n=A
		if A.endswith(g):J=max(1000,C//2-1000);M={_D:_F,U:J};A=A.replace(g,'');G=V;Y=_B
		elif A.endswith(h):J=max(1000,C//2-1000);M={_D:_F,U:J};A=A.replace(h,'');G=V;Y=_B
		elif A.endswith(i):J=max(1000,C//4-1000);M={_D:_F,U:J};A=A.replace(i,'');G=V;Y=_B
		else:M=P()
		N,S,K=B._format_tools_and_mcp_for_anthropic(D)
		if k:
			if N==P():N=[]
			N.append({_D:'code_execution_20241024',_H:'code_execution'})
		if l:
			if K:K[_I]+=',interleaved-thinking-2025-05-14'
			else:K[_I]='interleaved-thinking-2025-05-14'
		T=[]
		try:
			while _B:
				o=convert_messages_for_anthropic(R,response_format=H,cache_support=B._has_cache_support(A),media_converter=B.media_converter);Z={'model':A,'messages':o,j:G,'max_tokens':C,'tools':N,f:W,_J:M,'extra_headers':K if K else P(),**L}
				if S:Z['mcp_servers']=S
				p=B.client.beta.messages.stream if S else B.client.messages.stream
				with p(**Z)as a:
					for b in a:
						if b.type=='text':B.callback_manager.on_text_streaming(b.text)
					q=a.get_final_message()
				E=process_response_from_anthropic(anthropic_response=q,response_format=H);F.append(E);R.append(E);T.append(E)
				if not E.has(Action):break
				if not O:break
				if D:
					c=_C
					for I in E.blocks:
						if I.has_type(Action):
							d=I.media.action
							if hasattr(d,'tool_name'):
								e=D.get_tool(d.tool_name)
								if e and not e.is_builtin():c=_B;break
					if c:B._process_tool_calls(E,F,T,R,D)
					else:break
				else:break
			L.update({'model_name':n,j:G,'max_output_tokens':C,'toolset':D,'response_format':H});B.callback_manager.on_text_generation(messages=F,response=E,**L)
		except Exception as r:import traceback as s;s.print_exc();raise APIError(f"Anthropic generate text error: {r}")
		return T
```




==========
Prog. Lang. Code File: test_builtin_mcp_connector.py
Size: 592 bytes
==========

test_builtin_mcp_connector.py
```py
import anthropic
def test_generate_text_with_mcp():A='url';B=anthropic.Anthropic();C=B.beta.messages.create(model='claude-sonnet-4-20250514',max_tokens=1000,messages=[{'role':'user','content':'What tools do you have available?'}],mcp_servers=[{'type':A,A:'https://mcp.example.com/sse','name':'example-mcp','authorization_token':'YOUR_TOKEN'}],betas=['mcp-client-2025-04-04']);print(C)
```




==========
Prog. Lang. Code File: google_api.py
Size: 20.86 kilobytes
==========

google_api.py
```py
_D='model_name'
_C=False
_B=True
_A=None
import os
from typing import List,Optional,Sequence,Set,Type,Union
from PIL import Image as PilImage
from pydantic import BaseModel
from litemind.agent.messages.message import Message
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import ModelFeatures
from litemind.apis.callbacks.callback_manager import CallbackManager
from litemind.apis.default_api import DefaultApi
from litemind.apis.exceptions import APIError,APINotAvailableError
from litemind.apis.feature_scanner import get_default_model_feature_scanner
from litemind.apis.providers.google.utils.aggegate_chat_response import aggregate_chat_response
from litemind.apis.providers.google.utils.check_availability import check_gemini_api_availability
from litemind.apis.providers.google.utils.convert_messages import convert_messages_for_gemini
from litemind.apis.providers.google.utils.format_tools import format_tools_for_gemini
from litemind.apis.providers.google.utils.list_models import _get_gemini_models_list
from litemind.apis.providers.google.utils.process_response import process_response_from_gemini
from litemind.apis.providers.google.utils.response_to_object import response_to_object
from litemind.apis.tests.test_callback_manager import callback_manager
from litemind.media.media_base import MediaBase
from litemind.media.types.media_action import Action
from litemind.media.types.media_text import Text
from litemind.utils.json_to_object import json_to_object
class GeminiApi(DefaultApi):
	def __init__(A,api_key=_A,allow_media_conversions=_B,allow_media_conversions_with_models=_B,callback_manager=_A,**C):
		B=api_key;super().__init__(allow_media_conversions=allow_media_conversions,allow_media_conversions_with_models=allow_media_conversions_with_models,callback_manager=callback_manager)
		if B is _A:B=os.environ.get('GOOGLE_GEMINI_API_KEY')
		if not B:raise APIError('A valid GOOGLE_API_KEY is required for GeminiApi. Set GOOGLE_API_KEY in the environment or pass api_key explicitly.')
		A._api_key=B;A.kwargs=C
		try:A.feature_scanner=get_default_model_feature_scanner();import google.generativeai as D;D.configure(api_key=A._api_key,**A.kwargs);A._model_list=_get_gemini_models_list()
		except Exception as E:import traceback as F;F.print_exc();raise APINotAvailableError(f"Error initializing Gemini client: {E}")
	def check_availability_and_credentials(A,api_key=_A):B=check_gemini_api_availability(api_key=api_key);A.callback_manager.on_availability_check(B);import google.generativeai as C;C.configure(api_key=A._api_key,**A.kwargs);return B
	def list_models(C,features=_A,non_features=_A,media_types=_A):
		D=non_features;B=features
		try:
			B=ModelFeatures.normalise(B);D=ModelFeatures.normalise(D);A=list(C._model_list);A+=super().list_models();A=list(dict.fromkeys(A))
			if B:A=C._filter_models(A,features=B,non_features=D,media_types=media_types)
			C.callback_manager.on_model_list(A);return A
		except Exception:raise APIError('Error fetching model list from Google.')
	def get_best_model(B,features=_A,non_features=_A,media_types=_A,exclusion_filters=_A):
		D=non_features;C=features;C=ModelFeatures.normalise(C);D=ModelFeatures.normalise(D);A=B.list_models();A=B._filter_models(A,features=C,non_features=D,media_types=media_types,exclusion_filters=exclusion_filters)
		if A:E=A[0]
		else:E=_A
		B.callback_manager.on_best_model_selected(E);return E
	def has_model_support_for(C,features,media_types=_A,model_name=_A):
		D=media_types;B=model_name;A=features;A=ModelFeatures.normalise(A)
		if B is _A:B=C.get_best_model(features=A,media_types=D)
		if B is _A:return _C
		if super().has_model_support_for(features=A,media_types=D,model_name=B):return _B
		for E in A:
			if not C.feature_scanner.supports_feature(C.__class__,B,E):return _C
		return _B
	def _has_thinking_support(B,model_name):
		A=model_name
		if'models/gemini'not in A.lower()and'thinking'not in A.lower():return _C
		return _B
	def max_num_input_tokens(C,model_name=_A):
		A=model_name
		if A is _A:A=C.get_best_model()
		D=A.lower();from google.generativeai import list_models as E
		for B in E():
			if D==B.name.lower():return B.input_token_limit
		return super().max_num_input_tokens(model_name=A)
	def max_num_output_tokens(C,model_name=_A):
		A=model_name
		if A is _A:A=C.get_best_model()
		D=A.lower();from google.generativeai import list_models as E
		for B in E():
			if D==B.name.lower():return B.output_token_limit
		return super().max_num_output_tokens(model_name=A)
	def generate_text(C,messages,model_name=_A,temperature=.0,max_num_output_tokens=_A,toolset=_A,use_tools=_B,response_format=_A,**L):
		K=use_tools;H=temperature;F=toolset;E=max_num_output_tokens;D=messages;B=response_format;A=model_name;import google.generativeai as T;from google.generativeai import types as N;super().generate_text(messages=D,model_name=A,temperature=H,max_num_output_tokens=E,toolset=F,use_tools=K,response_format=B,**L)
		if A is _A:A=C._get_best_model_for_text_generation(D,F if K else _A,B)
		I=''
		for J in D:
			if J.role=='system':
				for O in J.blocks:
					if O.has_type(Text):I+=O.get_content()
		if C._has_thinking_support(A):I+='\nThink carefully step-by-step before responding: restate the input, analyze it, consider options, make a plan, and proceed methodically to your conclusion. \n';I+=f"All reasoning (thinking) which precedes the final answer must be enclosed within thinking tags.This is how your response should be formatted: <thinking> reasoning goes here... </thinking> final answer goes here...\n\n"
		P=C._preprocess_messages(messages=D,allowed_media_types=C._get_allowed_media_types_for_text_generation(model_name=A))
		if E is _A:E=C.max_num_output_tokens(A)
		if B is _A or F is not _A:Q=N.GenerationConfig(temperature=H,max_output_tokens=E)
		else:Q=N.GenerationConfig(temperature=H,max_output_tokens=E,response_mime_type='application/json',response_schema=B)
		U=format_tools_for_gemini(F);M=[]
		try:
			V=T.GenerativeModel(model_name=A,tools=U,generation_config=Q,system_instruction=I);W=V.start_chat()
			while _B:
				X=convert_messages_for_gemini(P);Y=W.send_message(X,stream=_B);Z=aggregate_chat_response(Y,C.callback_manager.on_text_streaming);G=process_response_from_gemini(Z,response_format=B);D.append(G);M.append(G)
				if not G.has(Action):break
				if not K:break
				C._process_tool_calls(G,D,M,P,F,set_preprocessed=_B)
			if B:
				R=D.copy();J=Message(role='user',text='Convert the answer above to JSON adhering to the following schema:\n{response_format.model_json_schema()}\n');R.append(J);S=response_to_object(messages=R,model_name=A,max_num_output_tokens=E,response_format=B)
				if S:json_to_object(G,B,S[0].get_content())
			L.update({_D:A,'temperature':H,'max_output_tokens':E,'toolset':F,'response_format':B});C.callback_manager.on_text_generation(response=G,messages=D,**L)
		except Exception as a:raise APIError(f"Gemini generate text error: {a}")
		return M
	def generate_image(C,positive_prompt,negative_prompt=_A,model_name=_A,image_width=512,image_height=512,preserve_aspect_ratio=_B,allow_resizing=_B,**H):
		G=image_height;F=image_width;E=negative_prompt;D=positive_prompt;B=model_name
		if B is _A:B=C.get_best_model(features=ModelFeatures.ImageGeneration)
		import google.generativeai as J;K=J.ImageGenerationModel(model_id=B);A=F/G
		if A==1:A='1:1'
		elif A==.75:A='3:4'
		elif A==1.33:A='4:3'
		elif A==.56:A='9:16'
		elif A==1.77:A='16:9'
		L=K.generate_images(prompt=D,number_of_images=1,safety_filter_level='block_only_high',person_generation='allow_adult',aspect_ratio=A,negative_prompt=E);I=L[0].image;H.update({'negative_prompt':E,_D:B,'image_width':F,'image_height':G,'preserve_aspect_ratio':preserve_aspect_ratio,'allow_resizing':allow_resizing});C.callback_manager.on_image_generation(prompt=D,image=I,**H);return I
	def embed_texts(F,texts,model_name=_A,dimensions=512,**D):
		C=dimensions;B=texts;A=model_name
		if A is _A:A=F.get_best_model(features=ModelFeatures.TextEmbeddings)
		if A in super().list_models():return super().embed_texts(texts=B,model_name=A,dimensions=C,**D)
		import google.generativeai as G;E=[]
		for H in B:I=G.embed_content(model=A,content=H,output_dimensionality=C);J=I['embedding'];E.append(J)
		D.update({_D:A,'dimensions':C});F.callback_manager.on_text_embedding(texts=B,embeddings=E,**D);return E
```




==========
Prog. Lang. Code File: ollama_api.py
Size: 17.52 kilobytes
==========

ollama_api.py
```py
_D='-thinking'
_C=False
_B=True
_A=None
from functools import lru_cache
from typing import Dict,List,Optional,Sequence,Set,Type,Union
from arbol import aprint
from litemind.agent.messages.message import Message
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import ModelFeatures
from litemind.apis.callbacks.callback_manager import CallbackManager
from litemind.apis.default_api import DefaultApi
from litemind.apis.exceptions import APIError,APINotAvailableError
from litemind.apis.feature_scanner import get_default_model_feature_scanner
from litemind.apis.providers.ollama.utils.aggregate_chat_responses import aggregate_chat_responses
from litemind.apis.providers.ollama.utils.check_availability import check_ollama_api_availability
from litemind.apis.providers.ollama.utils.convert_messages import convert_messages_for_ollama
from litemind.apis.providers.ollama.utils.format_tools import format_tools_for_ollama
from litemind.apis.providers.ollama.utils.list_models import _get_ollama_models_list
from litemind.apis.providers.ollama.utils.process_response import process_response_from_ollama
from litemind.media.media_base import MediaBase
from litemind.media.types.media_action import Action
from litemind.media.types.media_image import Image
from litemind.media.types.media_text import Text
class OllamaApi(DefaultApi):
	def __init__(A,host=_A,headers=_A,allow_media_conversions=_B,allow_media_conversions_with_models=_B,callback_manager=_A,**C):
		B=headers;super().__init__(allow_media_conversions=allow_media_conversions,allow_media_conversions_with_models=allow_media_conversions_with_models,callback_manager=callback_manager)
		try:A.feature_scanner=get_default_model_feature_scanner();from ollama import Client as D;A.client=D(host=host,headers=B,**C);A.host=host;A.headers=B;A._model_list=_get_ollama_models_list(A.client)
		except Exception as E:import traceback as F;F.print_exc();raise APINotAvailableError(f"Error initializing Ollama client: {E}")
	def check_availability_and_credentials(A,api_key=_A):B=check_ollama_api_availability(A.client);A.callback_manager.on_availability_check(B);return B
	def list_models(C,features=_A,non_features=_A,media_types=_A):
		D=non_features;B=features
		try:
			B=ModelFeatures.normalise(B);D=ModelFeatures.normalise(D);A=list(C._model_list);A+=super().list_models()
			if B:A=C._filter_models(A,features=B,non_features=D,media_types=media_types)
			C.callback_manager.on_model_list(A);return A
		except Exception:raise APIError('Error fetching model list from Ollama.')
	def get_best_model(B,features=_A,non_features=_A,media_types=_A,exclusion_filters=_A):
		D=non_features;C=features;C=ModelFeatures.normalise(C);D=ModelFeatures.normalise(D);A=B.list_models();A=B._filter_models(A,features=C,non_features=D,media_types=media_types,exclusion_filters=exclusion_filters)
		if A:E=A[0]
		else:E=_A
		B.callback_manager.on_best_model_selected(E);return E
	def has_model_support_for(C,features,media_types=_A,model_name=_A):
		D=media_types;B=model_name;A=features;A=ModelFeatures.normalise(A)
		if B is _A:B=C.get_best_model(features=A,media_types=D)
		if B is _A:return _C
		if super().has_model_support_for(features=A,media_types=D,model_name=B):return _B
		for E in A:
			if not C.feature_scanner.supports_feature(C.__class__,B,E):return _C
		return _B
	def _is_ollama_model(A,model_name):return':'in model_name
	def _has_thinking_support(A,model_name):
		if model_name.endswith(_D):return _B
		return _C
	@lru_cache
	def _has_tool_support(self,model_name):
		A=model_name
		if not self._is_ollama_model(A):return _C
		try:B=self.client.show(A).template;return'.Tools'in B
		except:return _C
	@lru_cache
	def max_num_input_tokens(self,model_name=_A):
		A=model_name;from ollama import ResponseError as D
		if A is _A:A=self.get_best_model(ModelFeatures.TextGeneration)
		try:
			B=self.client.show(A).modelinfo
			for C in B.keys():
				if'context_length'in C:return B[C]
		except D as E:aprint(f"Model {A} not found in Ollama: {E.error}");import traceback as F;F.print_exc()
		return 2500
	@lru_cache
	def max_num_output_tokens(self,model_name=_A):
		A=model_name;from ollama import ResponseError as D
		if A is _A:A=self.get_best_model(ModelFeatures.TextGeneration)
		try:
			B=self.client.show(A).modelinfo
			for C in B.keys():
				if'max_tokens'in C:return B[C]
		except D as E:aprint(f"Model {A} not found in Ollama: {E.error}");import traceback as F;F.print_exc()
		return 4096
	def generate_text(B,messages,model_name=_A,temperature=.0,max_num_output_tokens=_A,toolset=_A,use_tools=_B,response_format=_A,**I):
		S='temperature';R='system';L=use_tools;K=temperature;F=response_format;E=max_num_output_tokens;D=messages;C=toolset;A=model_name;super().generate_text(messages=D,model_name=A,temperature=K,max_num_output_tokens=E,toolset=C,use_tools=L,response_format=F,**I);from ollama import ResponseError as T
		if A is _A:A=B._get_best_model_for_text_generation(D,C if L else _A,F)
		U={Text}
		if B.has_model_support_for(model_name=A,features=ModelFeatures.Image):U.add(Image)
		G=B._preprocess_messages(messages=D,allowed_media_types=B._get_allowed_media_types_for_text_generation(model_name=A))
		if B._has_thinking_support(A):
			if A.endswith(_D):A=A[:-9]
			J=next((A for A in G if A.role==R),_A)
			if not J:J=Message(role=R,text='');G.insert(0,J)
			for O in J.blocks:
				if O.has_type(Text):M=O.media.text;M+='\n';M+='Think carefully step-by-step before responding: restate the input, analyze it, consider options, make a plan, and proceed methodically to your conclusion. \n';M+=f"All reasoning (thinking) which precedes the final answer must be enclosed within thinking tags: <thinking> reasoning goes here... </thinking> final answer here...\n\n";break
		if E is _A:E=B.max_num_output_tokens(A)
		V=format_tools_for_ollama(C)if C else _A;N=[]
		try:
			while _B:
				W=convert_messages_for_ollama(G,response_format=F);X=B.client.chat(model=A,messages=W,tools=V,options={S:K,'num_predict':E},stream=_B,**I);P=aggregate_chat_responses(X,callback=B.callback_manager.on_text_streaming);H=process_response_from_ollama(P,C,F);D.append(H);G.append(H);N.append(H)
				if not H.has(Action):break
				if not L:break
				B._process_tool_calls(H,D,N,G,C)
			I.update({S:K,'max_output_tokens':E,'toolset':C,'response_format':F});B.callback_manager.on_text_generation(response=P,messages=D,**I)
		except T as Q:raise APIError(f"Ollama generate text error: {Q.error} (status code: {Q.status_code})")
		return N
	def embed_texts(C,texts,model_name=_A,dimensions=512,**F):
		E=texts;D=dimensions;A=model_name
		if A is _A:A=C.get_best_model(features=ModelFeatures.TextEmbeddings)
		if A in super().list_models():return super().embed_texts(texts=E,model_name=A,dimensions=D,**F)
		from ollama import EmbedResponse;G=C.client.embed(model=A,input=E);B=G.embeddings
		if len(B[0])!=D:H=C._reduce_embeddings_dimension(B,D);B=H
		F.update({'model_name':A,'dimensions':D});C.callback_manager.on_text_embedding(texts=E,embeddings=B,**F);return B
```




==========
Prog. Lang. Code File: multi_image_test.py
Size: 2.43 kilobytes
==========

multi_image_test.py
```py
import pytest
from litemind.agent.messages.message import Message
from litemind.apis.model_features import ModelFeatures
from litemind.apis.providers.ollama.ollama_api import OllamaApi
from litemind.ressources.media_resources import MediaResources
def test_compare_images_with_separate_messages():
	G='user';C=OllamaApi()
	if not C.check_availability_and_credentials():pytest.skip(f"{OllamaApi.__name__} is not available. Skipping image tests.")
	D=C.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Image])
	if D is None:pytest.skip(f"{OllamaApi.__name__} does not support images. Skipping image tests.")
	print('\n'+D);B=[];H=Message(role='system');H.append_text('You are an omniscient all-knowing being called Ohmm');B.append(H);E=Message(role=G);E.append_text('Here is the first image.');J=MediaResources.get_local_test_image_uri('cat.jpg');E.append_image(J);B.append(E);F=Message(role=G);F.append_text('Here is the second image.');K=MediaResources.get_local_test_image_uri('panda.jpg');F.append_image(K);B.append(F);I=Message(role=G);I.append_text('Can you compare these two images? What is similar and what is different?');B.append(I);A=C.generate_text(messages=B,model_name=D)
	for L in B:print(L)
	A=A[-1].lower();assert('animal'in A or'character'in A or'subject'in A)and('cat'in A or'creature'in A)and'panda'in A
```




==========
Prog. Lang. Code File: openai_api.py
Size: 47.58 kilobytes
==========

openai_api.py
```py
_R='function_call'
_Q='tool_name'
_P='gpt-4o'
_O='o1-preview'
_N='o1-mini'
_M='o4-mini'
_L='gpt-4.1'
_K='response_format'
_J='input_text'
_I='model_name'
_H='assistant'
_G='message'
_F='output_text'
_E='text'
_D=False
_C=True
_B='type'
_A=None
import base64,json,os,tempfile
from typing import List,Optional,Sequence,Type,Union
from openai import OpenAI
from pydantic import BaseModel
from litemind.agent.messages.message import Message
from litemind.agent.messages.message_block import MessageBlock
from litemind.agent.tools.base_tool import BaseTool
from litemind.agent.tools.builtin_tools.mcp_tool import BuiltinMCPTool
from litemind.agent.tools.builtin_tools.web_search_tool import BuiltinWebSearchTool
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import ModelFeatures
from litemind.apis.callbacks.callback_manager import CallbackManager
from litemind.apis.default_api import DefaultApi
from litemind.apis.exceptions import APIError,APINotAvailableError
from litemind.apis.feature_scanner import get_default_model_feature_scanner
from litemind.apis.providers.openai.utils.check_availability import check_openai_api_availability
from litemind.apis.providers.openai.utils.list_models import _get_raw_openai_model_list,get_openai_model_list
from litemind.media.media_base import MediaBase
from litemind.media.types.media_action import Action
from litemind.media.types.media_image import Image
from litemind.media.types.media_text import Text
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
_1M_TOKENS=1048576
_O_SERIES_CTX=200000
_O_SERIES_OUT=100000
class OpenAIApi(DefaultApi):
	def __init__(A,api_key=_A,base_url=_A,allow_media_conversions=_C,allow_media_conversions_with_models=_C,callback_manager=_A,**C):
		B=api_key;super().__init__(allow_media_conversions=allow_media_conversions,allow_media_conversions_with_models=allow_media_conversions_with_models,callback_manager=callback_manager)
		if B is _A:B=os.environ.get('OPENAI_API_KEY')
		if B is _A:raise APIError('The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable')
		A.api_key=B;A.base_url=base_url;A.kwargs=C
		try:A.feature_scanner=get_default_model_feature_scanner();A.client=OpenAI(api_key=A.api_key,base_url=A.base_url,**A.kwargs);A._raw_model_list=_get_raw_openai_model_list(A.client);A._model_list=get_openai_model_list(A._raw_model_list)
		except Exception as D:import traceback as E;E.print_exc();raise APINotAvailableError(f"Error initializing OpenAI Response API client: {D}")
	def check_availability_and_credentials(A,api_key=_A):
		B=api_key
		if B is not _A:C=OpenAI(api_key=B,base_url=A.base_url,**A.kwargs)
		else:C=A.client
		D=check_openai_api_availability(C);A.callback_manager.on_availability_check(D);return D
	def list_models(C,features=_A,non_features=_A,media_types=_A):
		D=non_features;B=features
		try:
			B=ModelFeatures.normalise(B);D=ModelFeatures.normalise(D);A=list(C._model_list);A+=super().list_models()
			if B:A=C._filter_models(A,features=B,non_features=D,media_types=media_types)
			C.callback_manager.on_model_list(A);return A
		except Exception:raise APIError('Error fetching model list from OpenAI Response API.')
	def get_best_model(B,features=_A,non_features=_A,media_types=_A,exclusion_filters=_A):
		D=non_features;C=features;C=ModelFeatures.normalise(C);D=ModelFeatures.normalise(D);A=B.list_models();A=B._filter_models(A,features=C,non_features=D,media_types=media_types,exclusion_filters=exclusion_filters);E=_A
		if len(A)>0:E=A[0]
		B.callback_manager.on_best_model_selected(E);return E
	def has_model_support_for(C,features,media_types=_A,model_name=_A):
		D=media_types;B=features;A=model_name;B=ModelFeatures.normalise(B)
		if A is _A:A=C.get_best_model(features=B,media_types=D)
		if A is _A:return _D
		if super().has_model_support_for(features=B,media_types=D,model_name=A):return _C
		if A not in C._model_list:return _D
		for E in B:
			if not C.feature_scanner.supports_feature(C.__class__,A,E):return _D
		return _C
	def max_num_input_tokens(C,model_name=_A):
		B=model_name
		if B is _A:B=C.get_best_model()
		A=B.lower()
		if _L in A:return _1M_TOKENS
		if any(B in A for B in(_M,'o3','o1')):
			if _N in A or _O in A:return 128000
			return _O_SERIES_CTX
		if _P in A:return 128000
		if'computer-use-preview'in A:return 128000
		return 128000
	def max_num_output_tokens(C,model_name=_A):
		B=model_name
		if B is _A:B=C.get_best_model()
		A=B.lower()
		if _L in A:return 32768
		if _M in A or'o3'in A:return _O_SERIES_OUT
		if _N in A:return 65536
		if _O in A:return 32768
		if'o1'in A:return _O_SERIES_OUT
		if'gpt-4o-mini'in A:return 16384
		if _P in A:return 16384
		return 16384
	def _convert_messages_to_response_input(I,messages,is_tool_followup=_D):
		H='user';G='content';F='role';C=messages
		if len(C)==1 and len(C[0].blocks)==1:
			if C[0].blocks[0].has_type(Text):return C[0].blocks[0].get_content()
		D=[]
		for B in C:
			if B.role=='system':
				A=I._convert_message_blocks_to_response_content(B.blocks,is_input=_C,is_system=_C)
				if A:D.append({_B:_G,F:H,G:A})
			elif B.role==H:
				A=I._convert_message_blocks_to_response_content(B.blocks,is_input=_C)
				if A:D.append({_B:_G,F:H,G:A})
			elif B.role==_H:
				A=I._convert_message_blocks_to_response_content(B.blocks,is_input=_D)
				if A:D.append({_B:_G,F:_H,G:A})
			elif B.role=='tool':
				if not is_tool_followup:
					A=[]
					for J in B.blocks:
						if J.has_type(Action):
							E=J.get_content()
							if hasattr(E,_Q)and hasattr(E,'result'):K=f"[Tool {E.tool_name} returned: {E.result}]";A.append({_B:_J,_E:K})
					if A:D.append({_B:_G,F:H,G:A})
		return D
	def _convert_message_blocks_to_response_content(O,blocks,is_input=_C,is_system=_D):
		H='input_audio';D=is_input;from litemind.media.types.media_audio import Audio;from litemind.media.types.media_code import Code;from litemind.media.types.media_document import Document;from litemind.media.types.media_file import File;from litemind.media.types.media_json import Json;from litemind.media.types.media_object import Object;from litemind.media.types.media_table import Table;from litemind.media.types.media_video import Video;B=[]
		for C in blocks:
			A=C.media
			if C.has_type(Text):
				F=A.get_content()
				if is_system:F=f"[SYSTEM]: {F}"
				B.append({_B:_J if D else _F,_E:F})
			elif C.has_type(Image):
				if D:I=A.to_remote_or_data_uri();B.append({_B:'input_image','image_url':I})
				else:G=A.get_filename()if hasattr(A,'get_filename')else'image';B.append({_B:_F,_E:f"[Image: {G}]"})
			elif C.has_type(Audio):
				if D:
					try:J=A.get_raw_data();import base64 as K;L=K.b64encode(J).decode('utf-8');G=A.get_filename();M='mp3'if G.lower().endswith('.mp3')else'wav';B.append({_B:H,H:{'data':L,'format':M}})
					except Exception as P:B.append({_B:_J,_E:f"[Audio file: {A.get_filename()}]"})
				else:B.append({_B:_F,_E:f"[Audio: {A.get_filename()}]"})
			elif C.has_type(Action):
				if not D:
					E=A.get_content()
					if hasattr(E,_Q)and hasattr(E,'arguments'):N=f"[Called tool {E.tool_name} with arguments: {E.arguments}]";B.append({_B:_F,_E:N})
			else:raise ValueError(f"Unsupported media type in message block: {type(A).__name__}")
		return B
	def _convert_response_to_messages(J,response,response_format=_A):
		E=response_format;D=[]
		for A in response.output:
			if A.type==_G and A.role==_H:
				B=Message(role=_H)
				for F in A.content:
					if F.type==_F:
						C=F.text
						if E and C.strip().startswith('{'):
							try:G=json.loads(C);H=E(**G);B.append_object(H)
							except(json.JSONDecodeError,ValueError,TypeError)as I:print(f"Warning: Failed to parse structured output: {I}");B.append_text(C)
						else:B.append_text(C)
				D.append(B)
			elif A.type==_R:B=Message(role=_H);B.append_tool_call(tool_name=A.name,arguments=json.loads(A.arguments)if isinstance(A.arguments,str)else A.arguments,id=A.call_id);D.append(B)
			elif A.type in['web_search_call','file_search_call','computer_use_call']:0
		return D
	def _format_tools_for_response_api(D,toolset):
		B=[]
		for A in toolset.list_tools():
			if A.is_builtin():continue
			C={_B:'function','name':A.name,'description':A.description,'parameters':A.arguments_schema};B.append(C)
		return B
	def generate_text(B,messages,model_name=_A,temperature=.0,max_num_output_tokens=_A,toolset=_A,use_tools=_C,response_format=_A,**I):
		s='tools';r='effort';q='reasoning';p='text_format';o='input';n='model';f='temperature';e='max_output_tokens';Q=use_tools;L=max_num_output_tokens;K=messages;H=temperature;G=toolset;D=response_format;C=model_name;t=_D;g=_A;u=_D;v=_C;super().generate_text(messages=K,model_name=C,temperature=H,max_num_output_tokens=L,toolset=G,use_tools=Q,response_format=D,**I)
		if C is _A:C=B._get_best_model_for_text_generation(K,G if Q else _A,D)
		M=_A
		if'o1'in C or'o3'in C:
			H=_A;X=C.split('-')[-1]
			if X in['low','medium','high']:M=X;R=C.replace(f"-{X}",'')
			else:M=_A;R=C
		else:R=C
		Y=B._preprocess_messages(messages=K,allowed_media_types=B._get_allowed_media_types_for_text_generation(model_name=C))
		if L is _A:L=B.max_num_output_tokens(C)
		E=[]
		if Q:
			if G:
				E.extend(B._format_tools_for_response_api(G))
				for N in G.list_builtin_tools():
					if isinstance(N,BuiltinWebSearchTool):w=N;x=w.search_context_size;E.append({_B:'web_search_preview','search_context_size':x})
					elif isinstance(N,BuiltinMCPTool):S=N;E.append({_B:'mcp','server_label':S.server_name,'server_url':S.server_url,'headers':S.headers,'require_approval':'never','allowed_tools':S.allowed_tools})
					else:raise ValueError('Unknown built-in tool: {}'.format(N.name))
			if t:
				if not g:raise ValueError('vector_store_ids must be provided when use_file_search=True')
				E.append({_B:'file_search','vector_store_ids':g})
			if u:E.append({_B:'computer_use'})
		try:
			Z=[];a=_A;F=[]
			while _C:
				if a is _A:
					b=B._convert_messages_to_response_input(Y,is_tool_followup=_D);A={n:R,o:b,e:L}
					if D:A[p]=D
					if H is not _A:A[f]=H
					if M is not _A:A[q]={r:M}
					if E:A[s]=E
					A.update(I)
				else:
					A={n:R,o:b,'previous_response_id':a,e:L}
					if D:A[p]=D
					if H is not _A:A[f]=H
					if M is not _A:A[q]={r:M}
					if E:A[s]=E
					A.update(I)
				try:
					with B.client.responses.stream(**A)as h:
						for O in h:
							if _F in O.type:
								if hasattr(O,'delta'):B.callback_manager.on_text_streaming(fragment=O.delta,**I)
								elif hasattr(O,_E):B.callback_manager.on_text_streaming(fragment=O.text,**I)
						T=h.get_final_response()
				except AttributeError:T=B.client.responses.create(**A)
				F=B._convert_response_to_messages(T)
				if F:K.extend(F);Y.extend(F);Z.extend(F)
				c=[]
				for i in T.output:
					if i.type==_R:c.append(i)
				if c and Q:
					U=Message(role='tool');K.append(U);Y.append(U);Z.append(U);j=[]
					for P in c:
						V=P.name;k=json.loads(P.arguments)if isinstance(P.arguments,str)else P.arguments;l=P.call_id;m=G.get_tool(V)if G else _A
						if m:
							try:
								J=m.execute(**k)
								if not isinstance(J,str):J=json.dumps(J,default=str)
							except Exception as d:J=f"Function '{V}' error: {d}"
						else:J=f"(Tool '{V}' use requested, but tool not found.)"
						U.append_tool_use(tool_name=V,arguments=k,result=J,id=l);j.append({_B:'function_call_output','call_id':l,'output':J})
					b=j;a=T.id
				else:break
			if D and F:
				W=F[-1]
				if W.blocks and W.blocks[-1].has_type(Text):
					y=W.blocks[-1].get_content()
					try:z=json.loads(y);A0=D(**z);W.append_object(A0)
					except(json.JSONDecodeError,ValueError):pass
			I.update({_I:C,f:H,e:L,'toolset':G,_K:D,'stream':v})
			if F:B.callback_manager.on_text_generation(messages=K,response=F[-1],**I)
			return Z
		except Exception as d:import traceback as A1;A1.print_exc();raise APIError(f"OpenAI Response API generate text error: {d}")
	def transcribe_audio(B,audio_uri,model_name=_A,**D):
		C=audio_uri;A=model_name
		if A is _A:A=B.get_best_model(features=ModelFeatures.AudioTranscription)
		if A is _A or A in super().list_models():return super().transcribe_audio(audio_uri=C,model_name=A,**D)
		if'whisper'not in A:raise APIError(f"Model {A} does not support audio transcription.")
		F=uri_to_local_file_path(C)
		with open(F,'rb')as G:E=B.client.audio.transcriptions.create(model=A,file=G,response_format=_E)
		D.update({_I:A});B.callback_manager.on_audio_transcription(audio_uri=C,transcription=E,**D);return E
	def generate_audio(E,text,voice=_A,audio_format=_A,model_name=_A,**D):
		F=text;C=voice;B=audio_format;A=model_name
		if A is _A:A=E.get_best_model(features=ModelFeatures.AudioGeneration)
		if A is _A or A in super().list_models():return super().generate_audio(text=F,voice=C,audio_format=B,model_name=A,**D)
		if C is _A:C='onyx'
		if B is _A:B='mp3'
		I=E.client.audio.speech.create(model=A,voice=C,response_format=B,input=F,**D)
		with tempfile.NamedTemporaryFile(suffix=f".{B}",delete=_D)as G:I.write_to_file(G.name);H='file://'+G.name;D.update({'voice':C,'audio_format':B,_I:A});E.callback_manager.on_audio_generation(text=F,audio_uri=H,**D);return H
	def generate_image(C,positive_prompt,negative_prompt=_A,model_name=_A,image_width=512,image_height=512,preserve_aspect_ratio=_C,allow_resizing=_C,**B):
		Z='b64_json';Y='quality';X='gpt-image-1';W='dall-e-3';V='1024x1792';U='1792x1024';T='dall-e-2';O=allow_resizing;N=preserve_aspect_ratio;M=positive_prompt;L='1024x1024';J=negative_prompt;E=image_height;D=image_width;A=model_name
		if J:H=f"Image must consist in: {M}\nBut NOT of: {J}"
		else:H=M
		if A is _A:A=C.get_best_model(features=ModelFeatures.ImageGeneration)
		if A and A in C._model_list:
			try:P=C.client.responses.create(model=A,input=f"Generate an image: {H}",tools=[{_B:'image_generation'}]if'gpt-image'in A else _A,**B)
			except:pass
		if T in A:F=['256x256','512x512',L,U,V]
		elif W in A:F=[L,V,U]
		elif X in A:F=[L,'1024x1536','1536x1024']
		else:raise ValueError(f"Model {A} is not supported for image generation.")
		I=f"{D}x{E}"
		if I not in F:
			if not O:raise ValueError(f"Requested resolution {I} is not allowed.")
			Q=sorted(set(int(A.split('x')[0])for A in F));R=sorted(set(int(A.split('x')[1])for A in F));a=next((A for A in Q if A>=D),Q[-1]);b=next((A for A in R if A>=E),R[-1]);K=f"{a}x{b}"
		else:K=I
		if X in A:B[Y]='auto';B['output_format']='png'
		elif W in A:B[Y]='hd';B[_K]=Z
		elif T in A:B[_K]=Z
		P=C.client.images.generate(model=A,prompt=H,size=K,n=1,**B);c=P.data[0].b64_json;d=base64.b64decode(c);from io import BytesIO as e;from PIL import Image;from PIL.Image import Resampling as S;G=Image.open(e(d))
		if K!=I:
			if N:G.thumbnail((D,E),S.LANCZOS)
			else:G=G.resize((D,E),S.LANCZOS)
		B.update({_I:A,'negative_prompt':J,'image_width':D,'image_height':E,'preserve_aspect_ratio':N,'allow_resizing':O});C.callback_manager.on_image_generation(image=G,prompt=H,**B);return G
	def embed_texts(C,texts,model_name=_A,dimensions=512,**B):
		E=dimensions;D=texts;A=model_name
		if A is _A:A=C.get_best_model(features=ModelFeatures.TextEmbeddings)
		if A in super().list_models():return super().embed_texts(texts=D,model_name=A,dimensions=E,**B)
		G=C.client.embeddings.create(model=A,dimensions=E,input=D,**B);F=[A.embedding for A in G.data];B.update({_I:A,'dimensions':E});C.callback_manager.on_text_embedding(texts=D,embeddings=F,**B);return F
```




==========
Prog. Lang. Code File: test_apis_basics.py
Size: 7.12 kilobytes
==========

test_apis_basics.py
```py
import pytest
from arbol import aprint
from litemind import API_IMPLEMENTATIONS
from litemind.apis.base_api import ModelFeatures
from litemind.ressources.media_resources import MediaResources
@pytest.mark.parametrize('api_class',API_IMPLEMENTATIONS)
class TestBaseApiImplementationsBasics(MediaResources):
	def test_availability_and_credentials(C,api_class):A=api_class;B=A();assert B.check_availability_and_credentials()is True,f"{A.__name__}.check_api_key() should be True!"
	def test_model_list(G,api_class):
		A=api_class;C=A();D=C.list_models()
		for F in D:aprint(F)
		assert isinstance(D,list),f"{A.__name__}.model_list() should return a list!";assert len(D)>0,f"{A.__name__}.model_list() should not be empty!"
		for B in ModelFeatures:
			E=C.get_best_model(features=B)
			if E:assert C.has_model_support_for(model_name=E,features=B),f"{A.__name__}.get_best_model({B}) should return a model ({E}) that supports {B}!"
	def test_has_model_support_for(H,api_class):
		C=api_class;B=C()
		for A in ModelFeatures:
			print(f"Checking support for feature: {A} in {C}");D=B.get_best_model();print('\n'+D);F=B.has_model_support_for(model_name=D,features=A);assert isinstance(F,bool),f"{C.__name__}.has_model_support_for({A}) should return a bool.";D=B.get_best_model(features=A)
			if D:assert B.has_model_support_for(model_name=D,features=A)
			else:aprint(f"No model in {C} supports feature: {A}")
			G=B.list_models()
			for E in G:
				if B.has_model_support_for(model_name=E,features=A):aprint(E)
			print(f"Checked support for feature: {A} in {C}: all good!")
	def test_get_model_features(G,api_class):
		A=api_class;C=A();E=C.list_models()
		for D in E:
			B=C.get_model_features(model_name=D);print(f"\nModel: {D} Features: {B}");assert isinstance(B,list),f"{A.__name__}.get_model_features() should return a list!"
			for F in B:assert isinstance(F,ModelFeatures),f"{A.__name__}.get_model_features() should return a list of ModelFeatures!"
	def test_max_num_input_token(E,api_class):
		A=api_class;D=A();B=D.get_best_model(features=ModelFeatures.TextGeneration)
		if not B:pytest.skip(f"{A.__name__} does not support text generation. Skipping text generation tests.")
		print('\n'+B);C=D.max_num_input_tokens(model_name=B);print(f"\nMax input tokens: {C}");assert isinstance(C,int),f"{A.__name__}.max_num_input_token() should return an int!";assert C>=4096,f"{A.__name__}.max_num_input_token() should be a positive integer equal or above 4096!"
	def test_max_num_output_tokens(E,api_class):
		A=api_class;D=A();B=D.get_best_model(features=ModelFeatures.TextGeneration)
		if B is None:pytest.skip(f"{A.__name__} does not support text generation. Skipping text generation tests.")
		print('\n'+B);C=D.max_num_output_tokens(model_name=B);print(f"\nMax output tokens: {C}");assert isinstance(C,int),f"{A.__name__}.max_num_output_tokens() should return an int!";assert C>0,f"{A.__name__}.max_num_output_tokens() should be a positive integer!"
```

==========
Prog. Lang. Code File: test_apis_builtin_tools.py
Size: 5.40 kilobytes
==========

test_apis_builtin_tools.py
```py
_C='assistant'
_B='system'
_A='\n'
import pytest
from litemind import API_IMPLEMENTATIONS
from litemind.agent.messages.message import Message
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import ModelFeatures
from litemind.ressources.media_resources import MediaResources
@pytest.mark.parametrize('api_class',API_IMPLEMENTATIONS)
class TestBaseApiImplementationsBuiltinTools(MediaResources):
	def test_text_generation_with_builtin_web_search_tool(H,api_class):
		B=api_class;E=B();D=E.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.WebSearchTool])
		if D is None:pytest.skip(f"{B.__name__} does not support text generation and built-in web serach tool. Skipping tests.")
		print(_A+D);G=[Message(role=_B,text='You are a helpful assistant with access to a web search tool.'),Message(role='user',text="In which 'classe preparatoire' did Loic A. Royer study?")];F=ToolSet();F.add_builtin_web_search_tool();A=E.generate_text(model_name=D,messages=G,temperature=.0,toolset=F);assert len(A)==1,f"Expected only one message in the response.";A=A[0];print(_A+str(A));assert A.role==_C,f"{B.__name__} completion should return an 'assistant' role.";C=str(A).lower();assert any('janson'in C or'sailly'in C or'dresden'in C or'robotics'in C for A in A),f"{B.__name__} completion should mention 'Janson', 'Sailly', 'Dresden' or 'Robotics' in the response."
	def test_text_generation_with_builtin_mcp_tool(H,api_class):
		B=api_class;D=B();C=D.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.MCPTool])
		if C is None:pytest.skip(f"{B.__name__} does not support text generation and built-in MCP tool. Skipping tests.")
		print(_A+C);G=[Message(role=_B,text='You are a helpful assistant.'),Message(role='user',text='What transport protocols does the 2025-03-26 version of the MCP spec (modelcontextprotocol/modelcontextprotocol) support?')];E=ToolSet();E.add_builtin_mcp_tool(server_name='deepwiki',server_url='https://mcp.deepwiki.com/mcp',allowed_tools=['ask_question']);A=D.generate_text(model_name=C,messages=G,temperature=.0,toolset=E);assert len(A)==1,f"Expected only one message in the response.";A=A[0];print(_A+str(A));assert A.role==_C,f"{B.__name__} completion should return an 'assistant' role.";F=str(A).lower();assert any('stdio'in F or'streamable http transport'in F for A in A),f"{B.__name__} completion should mention 'stdio' or 'Streamable HTTP Transport' in the response."
```

==========
Prog. Lang. Code File: test_apis_describe.py
Size: 8.54 kilobytes
==========

test_apis_describe.py
```py
_D='OllamaApi'
_C=False
_B='\n'
_A=None
import pytest
from litemind import API_IMPLEMENTATIONS
from litemind.apis.base_api import ModelFeatures
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_video import Video
from litemind.ressources.media_resources import MediaResources
@pytest.mark.parametrize('api_class',API_IMPLEMENTATIONS)
class TestBaseApiImplementationsDescribe(MediaResources):
	def test_describe_image_if_supported(E,api_class):
		B=api_class;C=B();D=C.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Image])
		if D is _A:pytest.skip(f"{B.__name__} does not support images. Skipping image tests.")
		try:F=E.get_local_test_image_uri('future.jpeg');A=C.describe_image(F,model_name=D);print(_B+A);assert isinstance(A,str),f"{B.__name__}.describe_image() should return a string!";assert len(A)>0,f"{B.__name__}.describe_image() should return a non-empty string!";assert'robot'in A or'futuristic'in A or'sky'in A
		except:import traceback as G;G.print_exc();assert _C
	def test_describe_audio_if_supported(E,api_class):
		B=api_class;C=B();D=C.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Audio])
		if D is _A:pytest.skip(f"{B.__name__} does not support audio. Skipping audio tests.")
		try:F=E.get_local_test_audio_uri('harvard.wav');A=C.describe_audio(F,model_name=D);print(_B+A);assert isinstance(A,str),f"{B.__name__}.describe_audio() should return a string!";assert len(A)>0,f"{B.__name__}.describe_audio() should return a non-empty string!";A=A.lower();assert'smell'in A or'ham'in A or'beer'in A or'sentence'in A
		except:import traceback as G;G.print_exc();assert _C
	def test_describe_video_if_supported(E,api_class):
		B=api_class;C=B();D=C.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Video])
		if D is _A:pytest.skip(f"{B.__name__} does not support videos. Skipping video tests.")
		try:
			F=E.get_local_test_video_uri('lunar_park.mov');A=C.describe_video(F,model_name=D);print(_B+A);assert isinstance(A,str),f"{B.__name__}.describe_video() should return a string!";assert len(A)>0,f"{B.__name__}.describe_video() should return a non-empty string!";A=A.lower()
			if B.__name__==_D:assert'elephant'in A or'people'in A
			else:assert('roller coaster'in A or'amusement park'in A or'ride'in A)and('20th century'in A or'20th-century'in A or'earlier era'in A or'vintage'in A or'period'in A)
		except:import traceback as G;G.print_exc();assert _C
	def test_describe_document_if_supported(E,api_class):
		B=api_class;C=B();D=C.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Document])
		if D is _A:pytest.skip(f"{B.__name__} does not support documents. Skipping documents tests.")
		try:
			F=E.get_local_test_document_uri('noise2self_paper.pdf');A=C.describe_document(F,model_name=D);print(_B+A);assert isinstance(A,str),f"{B.__name__}.describe_document() should return a string!";assert len(A)>0,f"{B.__name__}.describe_document() should return a non-empty string!";A=A.lower()
			if B.__name__==_D:assert'document'in A or'citations'in A
			else:assert'j-invariance'in A or'noise'in A or'parameters'in A
		except:import traceback as G;G.print_exc();assert _C
```

==========
Prog. Lang. Code File: test_apis_documents.py
Size: 17.89 kilobytes
==========

test_apis_documents.py
```py
_K='artwork'
_J='landscape'
_I='What is the age of John?'
_H='You are a computer program that can read complex json strings and understand what they contain.'
_G='The response might lack in detail!! Please check the response for more details.'
_F='biology'
_E='OllamaApi'
_D='user'
_C='system'
_B=None
_A='\n'
from typing import Dict
import pytest
from litemind import API_IMPLEMENTATIONS
from litemind.agent.messages.message import Message
from litemind.apis.base_api import ModelFeatures
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.ressources.media_resources import MediaResources
@pytest.mark.parametrize('api_class',API_IMPLEMENTATIONS)
class TestBaseApiImplementationsDocuments(MediaResources):
	def test_text_generation_with_pdf_document(H,api_class):
		B=api_class
		if B.__name__==_E:pytest.skip(f"{B.__name__} does not have strong enough models for this test. Skipping.")
		F=B();C=F.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Image,Document])
		if C is _B:pytest.skip(f"{B.__name__} does not support documents. Skipping documents tests.")
		print(_A+C);D=[];G=Message(role=_C);G.append_text('You are a highly qualified scientist with extensive experience in Microscopy, Biology and Bioimage processing and analysis.');D.append(G);E=Message(role=_D);E.append_text('Can you write a review for the provided paper below? Please break down your comments into major and minor comments.');I=H.get_local_test_document_uri('intracktive_preprint.pdf');E.append_document(I);D.append(E);A=F.generate_text(messages=D,model_name=C);print(_A+str(A));A=A[-1];assert len(A)>0,f"{B.__name__}.completion() should return a non-empty string!";assert'microscopy'in A.lower()or _F in A.lower()or'lineage'in A.lower()
		if not('inTRACKtive'in A and'review'in A.lower()):print(_G)
	def test_text_generation_with_word_document(H,api_class):
		B=api_class
		if B.__name__==_E:pytest.skip(f"{B.__name__} does not have strong enough models for this test. Skipping.")
		F=B();C=F.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Image,Document])
		if C is _B:pytest.skip(f"{B.__name__} does not support documents. Skipping documents tests.")
		print(_A+C);D=[];G=Message(role=_C);G.append_text('You are a very very helpfull assistant.');D.append(G);E=Message(role=_D);E.append_text('Please summarise this document.');I=H.get_local_test_document_uri('cartographers_of_life.docx');E.append_document(I);D.append(E);A=F.generate_text(messages=D,model_name=C);print(_A+str(A));A=A[-1];assert len(A)>0,f"{B.__name__}.completion() should return a non-empty string!";assert'virology'in A.lower()or _F in A.lower()or'immunology'in A.lower()
		if not('Takahashi'in A and'vitae'in A.lower()):print(_G)
	def test_text_generation_with_webpage(H,api_class):
		B=api_class
		if B.__name__==_E:pytest.skip(f"{B.__name__} does not have strong enough models for this test. Skipping.")
		F=B();C=F.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Image,Document])
		if C is _B:pytest.skip(f"{B.__name__} does not support documents. Skipping documents tests.")
		print(_A+C);D=[];G=Message(role=_C);G.append_text('You are a highly qualified scientist with extensive experience in Zebrafish biology.');D.append(G);E=Message(role=_D);E.append_text('Can you summarise the contents of the webpage and what is known about this gene in zebrafish? Which tissue do you think this gene is expressed in?');E.append_document('https://zfin.org/ZDB-GENE-060606-1');D.append(E);A=F.generate_text(messages=D,model_name=C);print(_A+str(A));A=A[-1];assert len(A)>0,f"{B.__name__}.completion() should return a non-empty string!";assert'ZFIN'in A or'COMP'in A or'zebrafish'in A
	def test_text_generation_with_json(I,api_class):
		B=api_class;F=B();C=F.get_best_model(ModelFeatures.TextGeneration)
		if C is _B:pytest.skip(f"{B.__name__} does not support text generation. Skipping tests.")
		print(_A+C);D=[];G=Message(role=_C);G.append_text(_H);D.append(G);H='\n        {\n          "name": "John Doe",\n          "age": 30,\n          "cars": {\n          "car1": "Ford",\n          "car2": "BMW",\n          "car3": "Fiat"\n          }\n        }\n        ';E=Message(role=_D);E.append_json(H);E.append_text(_I);D.append(E);A=F.generate_text(messages=D,model_name=C);print(_A+str(A));A=A[-1];assert len(A)>0,f"{B.__name__}.completion() should return a non-empty string!";assert'30'in A
	def test_text_generation_with_object(J,api_class):
		B=api_class
		try:from pydantic import BaseModel
		except ImportError:pytest.skip('Pydantic is not installed. Skipping tests.')
		F=B();C=F.get_best_model(ModelFeatures.TextGeneration)
		if C is _B:pytest.skip(f"{B.__name__} does not support text generation. Skipping tests.")
		print(_A+C);D=[];G=Message(role=_C);G.append_text(_H);D.append(G);from pydantic import BaseModel
		class H(BaseModel):name:str;age:int;cars:Dict[str,str]
		I=H(name='John Doe',age=30,cars={'car1':'Ford','car2':'BMW','car3':'Fiat'});E=Message(role=_D);E.append_object(I);E.append_text(_I);D.append(E);A=F.generate_text(messages=D,model_name=C);print(_A+str(A));A=A[-1];assert len(A)>0,f"{B.__name__}.completion() should return a non-empty string!";assert'30'in A
	def test_text_generation_with_csv(H,api_class):
		B=api_class;F=B();C=F.get_best_model(ModelFeatures.TextGeneration)
		if C is _B:pytest.skip(f"{B.__name__} does not support text generation. Skipping tests.")
		print(_A+C);D=[];G=Message(role=_C);G.append_text('You are a highly qualified data scientist.');D.append(G);I=H.get_local_test_table_uri('spreadsheet.csv');E=Message(role=_D);E.append_text('List all items sold by rep Carl Jackson in the provided table.');E.append_table(I);D.append(E);A=F.generate_text(messages=D,model_name=C);print(_A+str(A));A=A[-1];assert len(A)>0,f"{B.__name__}.completion() should return a non-empty string!";assert'Binders'in A or'SAFCO'in A
	def test_text_generation_with_archive(H,api_class):
		B=api_class
		if B.__name__==_E:pytest.skip(f"{B.__name__} does not have strong enough models for this test. Skipping.")
		F=B();C=F.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Image,Document])
		if C is _B:pytest.skip(f"{B.__name__} does not support text generation. Skipping tests.")
		print(_A+C);D=[];G=Message(role=_C);G.append_text('You are a highly qualified historian and literature expert');D.append(G);I=H.get_local_test_archive_uri('alexander.zip');E=Message(role=_D);E.append_text('Make a one paragraph summary of the provided material, plus a list of all documents provided.');E.append_archive(I);D.append(E);A=F.generate_text(messages=D,model_name=C);print(_A+str(A));A=A[-1];assert len(A)>0,f"{B.__name__}.completion() should return a non-empty string!"
		if B.__name__==_E:assert'image'in A or _J in A or _K in A
		else:assert'Alexander'in A or'Aristotle'in A
	def test_text_generation_with_folder(H,api_class):
		B=api_class;F=B();C=F.get_best_model([ModelFeatures.TextGeneration],media_types=[Image,Document])
		if C is _B:pytest.skip(f"{B.__name__} does not support text generation. Skipping tests.")
		print(_A+C);D=[];G=Message(role=_C);G.append_text('You are a highly qualified at comparing images and documents.');D.append(G);I=H.get_local_test_folder_path('images');E=Message(role=_D);E.append_text('Make a one paragraph summary of the provided material, compare the files provided, and make a list of all documents provided.');E.append_folder(I);D.append(E);A=F.generate_text(messages=D,model_name=C);print(_A+str(A));A=A[-1];assert len(A)>0,f"{B.__name__}.completion() should return a non-empty string!"
		if B.__name__==_E:assert _K in A or'futuristic'in A or _J in A or'humanoid'in A
		else:assert'beach'in A or'diverse'in A or'Python'in A or'ball'in A
```

==========
Prog. Lang. Code File: test_apis_embeddings.py
Size: 7.85 kilobytes
==========

test_apis_embeddings.py
```py
_G='The embeddings should be a list of length 512.'
_F='The embeddings should be a list of length 1.'
_E='Each embedding should be a list.'
_D='The embeddings should be a list of length 2.'
_C='Each value in the embedding should be a float.'
_B='The embeddings should be a list.'
_A=None
from typing import Sequence
import pytest
from litemind import API_IMPLEMENTATIONS
from litemind.apis.base_api import ModelFeatures
from litemind.ressources.media_resources import MediaResources
@pytest.mark.parametrize('api_class',API_IMPLEMENTATIONS)
class TestBaseApiImplementationsEmbeddings(MediaResources):
	def test_text_embedding(I,api_class):
		C=api_class;D=C();E=D.get_best_model(ModelFeatures.TextEmbeddings)
		if E is _A:pytest.skip(f"{C.__name__} does not support embeddings. Skipping tests.")
		F=['Hello, world!','Testing embeddings.'];A=D.embed_texts(texts=F,model_name=E,dimensions=512);assert A is not _A,'The embeddings should not be None.';assert isinstance(A,Sequence),_B;assert len(A)==2,_D
		for B in A:
			assert isinstance(B,Sequence),_E;assert len(B)==512,'Each embedding should be of length 512 as requested.'
			for G in B:assert isinstance(G,float),_C
		import numpy as H;A=H.array(A);print(A.shape)
	def test_audio_embedding(J,api_class):
		C=api_class;D=C();E=D.get_best_model(ModelFeatures.AudioEmbeddings)
		if E is _A:pytest.skip(f"{C.__name__} does not support audio embeddings. Skipping tests.")
		F=MediaResources.get_local_test_audio_uri('harvard.wav');G=MediaResources.get_local_test_audio_uri('preamble.wav');A=D.embed_audios(audio_uris=[F,G],model_name=E,dimensions=512);assert isinstance(A,list),_B;assert len(A)==2,_D
		for B in A:
			assert isinstance(B,list),_E;assert len(B)==512,'Each embedding should be of length 512 as reequested.'
			for H in B:assert isinstance(H,float),_C
		import numpy as I;A=I.array(A);print(A.shape)
	def test_video_embedding(C,api_class):
		D=api_class;E=D();B=E.get_best_model(ModelFeatures.VideoEmbeddings)
		if B is _A:pytest.skip(f"{D.__name__} does not support video embeddings. Skipping tests.")
		print(f"Embedding model name: {B}");F=C.get_local_test_video_uri('flying.mp4');G=C.get_local_test_video_uri('lunar_park.mp4');A=E.embed_videos(video_uris=[F,G],model_name=B,dimensions=512);assert isinstance(A,list),_B;assert len(A)==2,_F;assert len(A[0])==512,_G
		for H in A[0]:assert isinstance(H,float),_C
		import numpy as I;A=I.array(A);print(A.shape)
	def test_document_embedding(C,api_class):
		D=api_class;E=D();B=E.get_best_model(ModelFeatures.DocumentEmbeddings)
		if B is _A:pytest.skip(f"{D.__name__} does not support document embeddings. Skipping tests.")
		print(f"Embedding model name: {B}");F=C.get_local_test_document_uri('intracktive_preprint.pdf');G=C.get_local_test_document_uri('low_discrepancy_sequence.pdf');A=E.embed_documents(document_uris=[F,G],model_name=B,dimensions=512);assert isinstance(A,list),_B;assert len(A)==2,_F;assert len(A[0])==512,_G
		for H in A[0]:assert isinstance(H,float),_C
		import numpy as I;A=I.array(A);print(A.shape)
```

==========
Prog. Lang. Code File: test_apis_generate_multimodal.py
Size: 5.95 kilobytes
==========

test_apis_generate_multimodal.py
```py
from io import BytesIO
from pathlib import Path
import pytest
from PIL import Image
from litemind import API_IMPLEMENTATIONS
from litemind.apis.base_api import ModelFeatures
from litemind.apis.tests.utils.levenshtein import levenshtein_distance
from litemind.ressources.media_resources import MediaResources
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
@pytest.mark.parametrize('api_class',API_IMPLEMENTATIONS)
class TestBaseApiImplementationsGenerateMultimodal(MediaResources):
	def test_generate_audio(I,api_class):
		E=api_class;B=E();C=B.get_best_model(ModelFeatures.AudioGeneration)
		if C is None:pytest.skip(f"{E.__name__} does not support audio generation. Skipping tests.")
		print('Audio generation model name: ',C);D='Success is the ability to go from one failure to another with no loss of enthusiasm';print(f"Text to generate audio from: '{D}'");F=B.generate_audio(model_name=C,text=D);A=uri_to_local_file_path(F);A=Path(A);assert A.exists(),'The generated audio file should exist.';assert A.stat().st_size>0,'The generated audio file should not be empty.';assert A.suffix=='.mp3','The generated audio file should be an MP3 file.'
		if B.has_model_support_for(model_name=C,features=ModelFeatures.AudioConversion):G=B.transcribe_audio(F);H=levenshtein_distance(D,G);assert H<=len(D.split()),'The edit distance between the generated audio and the original text should be small.';print(f"Transcription: \n'{G}'")
	def test_generate_image(O,api_class):
		I='PNG';F=api_class;A=F();D=A.get_best_model(ModelFeatures.ImageGeneration)
		if not A.has_model_support_for(model_name=D,features=ModelFeatures.ImageGeneration):pytest.skip(f"{F.__name__} does not support image generation. Skipping tests.")
		print('Image generation model name: ',D);J='Please draw me a white siamese cat';K=1024;L=1024;E=A.generate_image(model_name=D,positive_prompt=J,image_width=K,image_height=L,preserve_aspect_ratio=True,allow_resizing=True);assert isinstance(E,Image.Image),'The generated image should be a PIL Image instance.';G=BytesIO();E.save(G,format=I);G.seek(0);import tempfile as M;H=M.NamedTemporaryFile(suffix='.png').name;E.save(H,format=I);N='file://'+H;C=A.get_best_model(ModelFeatures.Image);print('Image model name: ',C)
		if C and A.has_model_support_for(model_name=C,features=ModelFeatures.Image):B=A.describe_image(model_name=C,image_uri=N);print(f"Description of the generated image: \n{B}");assert isinstance(B,str),'The description should be a string.';assert len(B)>0,'The description should not be empty.';assert'cat'in B.lower(),"The description should mention a 'cat'.";assert'white'in B.lower()or'blue'in B.lower(),"The description should mention 'white' or 'blue'."
```

==========
Prog. Lang. Code File: test_apis_multimodal_inputs.py
Size: 18.06 kilobytes
==========

test_apis_multimodal_inputs.py
```py
_H='character'
_G='OllamaApi'
_F='Can you describe what you see in the image?'
_E=None
_D='user'
_C='You are an omniscient all-knowing being called Ohmm'
_B='system'
_A='\n'
import pytest
from litemind import API_IMPLEMENTATIONS
from litemind.agent.messages.message import Message
from litemind.apis.base_api import ModelFeatures
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_video import Video
from litemind.ressources.media_resources import MediaResources
@pytest.mark.parametrize('api_class',API_IMPLEMENTATIONS)
class TestBaseApiImplementationsMultimodalInputs(MediaResources):
	def test_text_generation_with_image_url(J,api_class):
		E=api_class;F=E();C=F.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Image])
		if not C:pytest.skip(f"{E.__name__} does not support images. Skipping image tests.")
		print(_A+C);G='https://upload.wikimedia.org/wikipedia/commons/thumb/3/3e/Einstein_1921_by_F_Schmutzer_-_restoration.jpg/456px-Einstein_1921_by_F_Schmutzer_-_restoration.jpg';print(G);B=[];H=Message(role=_B);H.append_text(_C);B.append(H);D=Message(role=_D);D.append_text(_F);D.append_image(G);B.append(D);A=F.generate_text(messages=B,model_name=C)
		for I in B:print(I)
		A=A[-1];assert'sepia'in A or'chalkboard'in A or'Einstein'in A or'black-and-white'in A or'photograph'in A
	def test_text_generation_with_png_image_path(H,api_class):
		C=api_class;E=C();B=E.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Image])
		if B is _E:pytest.skip(f"{C.__name__} does not support images. Skipping image tests.")
		print(_A+B)
		if not B or not E.has_model_support_for(model_name=B,features=[ModelFeatures.TextGeneration,ModelFeatures.Image]):pytest.skip(f"{C.__name__} does not support images. Skipping image tests.")
		D=[];G=Message(role=_B);G.append_text(_C);D.append(G);F=Message(role=_D);F.append_text(_F);I=H.get_local_test_image_uri('python.png');F.append_image(I);D.append(F);A=E.generate_text(messages=D,model_name=B)
		for J in D:print(J)
		A=A[-1]
		if C.__name__==_G:assert'blue'in A or'logo'in A
		else:assert'snake'in A or'serpent'in A or'python'in A
	def test_text_generation_with_jpg_image_path(H,api_class):
		E=api_class;F=E();C=F.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Image])
		if C is _E:pytest.skip(f"{E.__name__} does not support images. Skipping image tests.")
		print(_A+C);B=[];G=Message(role=_B);G.append_text(_C);B.append(G);D=Message(role=_D);D.append_text(_F);I=H.get_local_test_image_uri('future.jpeg');D.append_image(I);B.append(D);A=F.generate_text(messages=B,model_name=C)
		for J in B:print(J)
		A=A[-1];assert'robot'in A or'futuristic'in A or'sky'in A
	def test_text_generation_with_webp_image_path(H,api_class):
		E=api_class;F=E();C=F.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Image])
		if C is _E:pytest.skip(f"{E.__name__} does not support images. Skipping image tests.")
		print(_A+C);B=[];G=Message(role=_B);G.append_text(_C);B.append(G);D=Message(role=_D);D.append_text(_F);I=H.get_local_test_image_uri('beach.webp');D.append_image(I);B.append(D);A=F.generate_text(messages=B,model_name=C)
		for J in B:print(J)
		A=A[-1];assert'beach'in A or'palm'in A or'sunset'in A
	def test_text_generation_with_gif_image_path(H,api_class):
		E=api_class;F=E();C=F.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Image])
		if C is _E:pytest.skip(f"{E.__name__} does not support images. Skipping image tests.")
		print(_A+C);B=[];G=Message(role=_B);G.append_text(_C);B.append(G);D=Message(role=_D);D.append_text(_F);I=H.get_local_test_image_uri('field.gif');D.append_image(I);B.append(D);A=F.generate_text(messages=B,model_name=C)
		for J in B:print(J)
		A=A[-1];assert'field'in A or'blue'in A or'stars'in A
	def test_text_generation_with_multiple_images(F,api_class):
		I='panda';D=api_class;G=D();E=G.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Image])
		if E is _E:pytest.skip(f"{D.__name__} does not support images. Skipping image tests.")
		print(_A+E);B=[];H=Message(role=_B);H.append_text(_C);B.append(H);C=Message(role=_D);C.append_text('Can you compare these two images? What is similar and what is different?');J=F.get_local_test_image_uri('cat.jpg');K=F.get_local_test_image_uri('panda.jpg');C.append_image(J);C.append_image(K);B.append(C);A=G.generate_text(messages=B,model_name=E)
		for L in B:print(L)
		A=A[-1]
		if D.__name__==_G:assert I in A
		assert('animal'in A or _H in A or'subject'in A)and('cat'in A or'creature'in A)and I in A
	def test_text_generation_with_audio_path(H,api_class):
		E=api_class;F=E();C=F.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Audio])
		if C is _E:pytest.skip(f"{E.__name__} does not support audio. Skipping audio tests.")
		print(_A+C);B=[];G=Message(role=_B);G.append_text(_C);B.append(G);D=Message(role=_D);D.append_text('Can you describe what you heard in the audio file?');I=H.get_local_test_audio_uri('harvard.wav');D.append_audio(I);B.append(D);A=F.generate_text(messages=B,model_name=C)
		for J in B:print(J)
		A=A[-1];A=str(A).lower();assert'smell'in A or'ham'in A or'beer'in A or'reading'in A or'test passage'in A
	def test_text_generation_with_audio_url(I,api_class):
		E=api_class;F=E();C=F.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Audio])
		if C is _E:pytest.skip(f"{E.__name__} does not support audio. Skipping audio tests.")
		print(_A+C);B=[];G=Message(role=_B);G.append_text(_C);B.append(G);D=Message(role=_D);D.append_text('Can you describe in detail what is said the following audio file?');D.append_audio('https://salford.figshare.com/ndownloader/files/14630270');B.append(D);A=F.generate_text(messages=B,model_name=C)
		for H in B:print(H)
		A=A[-1];assert'canoe'in A or'chicken'in A or'hours'in A
	def test_text_generation_with_video_path(H,api_class):
		E=api_class;F=E();C=F.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Video])
		if C is _E:pytest.skip(f"{E.__name__} does not support videos. Skipping video tests.")
		print(_A+C);B=[];G=Message(role=_B);G.append_text(_C);B.append(G);D=Message(role=_D);D.append_text('Can you describe what you see in the video?');I=H.get_local_test_video_uri('flying.mp4');D.append_video(I);B.append(D);A=F.generate_text(messages=B,model_name=C)
		for J in B:print(J)
		A=A[-1];assert'disc'in A or'circular'in A or'saucer'in A or'rotor'in A or'curved'in A or'vehicle'in A or'aircraft'in A or'experimental'in A or'hovering'in A or'hover'in A or'flying'in A or'spacecraft'in A
	def test_text_generation_with_video_url(N,api_class):
		L='bunny';K='rabbit';J='animated';I='cartoon';C=api_class;F=C();D=F.get_best_model(features=[ModelFeatures.TextGeneration],media_types=[Video])
		if D is _E:pytest.skip(f"{C.__name__} does not support videos. Skipping video tests.")
		print(_A+D);G='https://ia803405.us.archive.org/27/items/archive-video-files/test.mp4';print(G);B=[];H=Message(role=_B);H.append_text(_C);B.append(H);E=Message(role=_D);E.append_text('Can you give a detailed description what you see in the following video?');E.append_video(G);B.append(E);A=F.generate_text(messages=B,model_name=D)
		for M in B:print(M)
		A=A[-1]
		if C.__name__==_G:assert'image'in A or'video'in A or I in A or J in A or K in A or L in A or _H in A
		else:assert K in A or L in A or I in A or J in A
```

==========
Prog. Lang. Code File: test_apis_text_generation.py
Size: 22.55 kilobytes
==========

test_apis_text_generation.py
```py
_J='November'
_I='November 15, 2024'
_H='When will my order order_12345 be delivered?'
_G='Fetch the delivery date for a given order ID'
_F='assistant'
_E='system'
_D='2024-11-15'
_C=None
_B='user'
_A='\n'
from datetime import datetime
import pytest
from pydantic import BaseModel
from litemind import API_IMPLEMENTATIONS
from litemind.agent.messages.message import Message
from litemind.agent.tools.toolset import ToolSet
from litemind.apis.base_api import ModelFeatures
from litemind.media.types.media_action import Action
from litemind.media.types.media_object import Object
from litemind.media.types.media_text import Text
from litemind.ressources.media_resources import MediaResources
@pytest.mark.parametrize('api_class',API_IMPLEMENTATIONS)
class TestBaseApiImplementationsTextGeneration(MediaResources):
	def test_text_generation_simple(F,api_class):
		B=api_class;D=B();C=D.get_best_model(ModelFeatures.TextGeneration,non_features=ModelFeatures.Thinking)
		if C is _C:pytest.skip(f"{B.__name__} does not support text generation. Skipping tests.")
		print(_A+C);E=[Message(role=_E,text='You are an omniscient all-knowing being called Ohmm'),Message(role=_B,text="I am 'The User'."),Message(role=_B,text='Who are you?')];A=D.generate_text(model_name=C,messages=E,temperature=.7);assert len(A)==1,f"Expected only one message in the response.";A=A[0];print(_A+str(A));assert A.role==_F,f"{B.__name__} completion should return an 'assistant' role.";assert'I am 'in A or"I'm"in A,f"Expected 'I am' or 'I'm' in the output of {B.__name__}.completion()"
	def test_text_generation_prefill(G,api_class):
		B=api_class;D=B();C=D.get_best_model(ModelFeatures.TextGeneration,non_features=ModelFeatures.Thinking)
		if C is _C:pytest.skip(f"{B.__name__} does not support text generation. Skipping tests.")
		print(_A+C);E=[Message(role=_E,text='You are very good at Math.'),Message(role=_B,text='What is 12+17 equal to? Please give the answer in parentheses.'),Message(role=_F,text='(')];A=D.generate_text(model_name=C,messages=E,temperature=.0);print(_A)
		for F in E:print(F)
		assert len(A)==1,f"Expected only one message in the response.";A=A[0];assert A.role==_F,f"{B.__name__} completion should return an 'assistant' role.";assert'29'in A,f"Expected '29' in the output of {B.__name__}.completion()"
		if not str(A[0]).startswith('29)'):print("Model does not support strict prefill behaviour: The response should start with '29)'")
	def test_text_generation_structured_output(K,api_class):
		A=api_class;F=A();D=F.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.StructuredTextGeneration])
		if D is _C:pytest.skip(f"{A.__name__} does not support text generation. Skipping tests.")
		print(_A+D);I='\n        NAZA awarded the aerospace company HeedLock Parthin, which also makes U.S. fighter jets, \n        a $247.5 million contract to build the X-15 craft, and as the images below show, \n        the plane is in its final testing stages before taking flight over the California desert. \n        Lockheed posted the image below on Jan. 24, showing burning gases shooting out the back of the engine. \n        NAZA noted in December that it was now running afterburner engine tests, \n        which gives an aircraft the thrust it needs to reach supersonic speeds of over some 767 mph.\n        '
		class G(BaseModel):buyer:str;contractor:str;sum_in_dollars:int;item_type:str
		H=[Message(role=_E,text='You are an accountant that likes to turn textual information into well structured information'),Message(role=_B,text=f"Please transcribe the following text adhering to the required format: \n```\n{I}\n```\n")];B=F.generate_text(model_name=D,messages=H,temperature=.7,response_format=G);print(_A)
		for J in H:print(J)
		assert len(B)==1,f"Expected only one message in the response.";B=B[0];assert B.role==_F,f"{A.__name__} completion should return an 'assistant' role.";E=B[-1];assert E.has_type(Object),f"{A.__name__} completion should return an object.";assert isinstance(E.get_content(),G),f"{A.__name__} completion should return an object of type Order.";C=E.get_content();assert C.buyer=='NAZA';assert C.contractor=='HeedLock Parthin';assert C.sum_in_dollars==247500000;assert'X-15'in C.item_type;print(C)
	def test_text_generation_with_simple_parameterless_tool(J,api_class):
		B=api_class;D=B()
		def E():return datetime.now().strftime('%Y-%m-%d')
		F=ToolSet();F.add_function_tool(E,'Fetch the current date');C=D.get_best_model([ModelFeatures.Tools,ModelFeatures.TextGeneration])
		if C is _C:pytest.skip(f"{B.__name__} does not support text generation and tools. Skipping tests.")
		print(_A+C);H=Message(role=_B,text='What is the current date? (Reply in %Y-%m-%d format)');G=[H];A=D.generate_text(model_name=C,messages=G,toolset=F);print(_A)
		for I in G:print(I)
		assert len(A)==3,f"Expected three message in the response.";assert A[-2][0].has_type(Action);A=A[-1];assert E()in A,f"The response of {B.__name__} should contain the delivery date."
	def test_text_generation_with_simple_toolset(J,api_class):
		B=api_class;D=B()
		def G(order_id):return _D
		E=ToolSet();E.add_function_tool(G,_G);C=D.get_best_model([ModelFeatures.Tools,ModelFeatures.TextGeneration])
		if C is _C:pytest.skip(f"{B.__name__} does not support text generation and tools. Skipping tests.")
		print(_A+C);H=Message(role=_B,text=_H);F=[H];A=D.generate_text(model_name=C,messages=F,toolset=E);print(_A)
		for I in F:print(I)
		assert len(A)==3,f"Expected three message in the response.";assert A[-2][0].has_type(Action);A=A[-1];assert _D in A or _I in A or _J in A and'15'in A and'2024'in A,f"The response of {B.__name__} should contain the delivery date."
	def test_text_generation_with_simple_toolset_and_struct_output(N,api_class):
		B=api_class;E=B()
		def J(order_id):return _D
		F=ToolSet();F.add_function_tool(J,_G);D=E.get_best_model([ModelFeatures.Tools,ModelFeatures.TextGeneration])
		if D is _C:pytest.skip(f"{B.__name__} does not support text generation and tools. Skipping tests.")
		print(_A+D)
		class G(BaseModel):order_id:str;delivery_date:str
		K=Message(role=_B,text=_H);H=[K];A=E.generate_text(model_name=D,messages=H,toolset=F,response_format=G);print(_A)
		for L in H:print(L)
		assert len(A)>=3,f"Expected three message in the response.";assert A[-2][0].has_type(Action);A=A[-1];M=str(A);assert _D in M,f"The response of {B.__name__} should contain the delivery date.";I=A[-1];assert isinstance(I.get_content(),G),f"The response of {B.__name__} should be of type OrderInfo.";C=I.get_content();print(C);assert C.order_id=='order_12345';assert C.delivery_date==_D;print(C)
	def test_text_generation_with_complex_toolset(M,api_class):
		H='Olea Table';D=api_class;G=D()
		def I(order_id):return _D
		def J(product_id):A=product_id;A=int(A);B={1393:H,84773:'Fluff Phone'};return B[A]
		def K(store_id,product_id):return'42'
		C=ToolSet();C.add_function_tool(I,'Fetch the delivery date given the order ID');C.add_function_tool(J,'Fetch the product name given the product ID');C.add_function_tool(K,'Fetch the number of items available given a product ID and store ID.');E=G.get_best_model([ModelFeatures.Tools,ModelFeatures.TextGeneration])
		if E is _C:pytest.skip(f"{D.__name__} does not support text generation and tools. Skipping tests.")
		print(_A+E);F=Message(role=_B,text="When will my order 'order_12345' be delivered?");B=[F];A=G.generate_text(model_name=E,messages=B,toolset=C);assert len(B)==4,'We should have four messages in the list. The user message and the response message.';A=str(A);assert _D in A or _I in A or _J in A and'15'in A and'2024'in A,f"The response of {D.__name__} should contain the delivery date.";F=Message(role=_B,text='What is the name of product 1393?');B+=[F];A=G.generate_text(model_name=E,messages=B,toolset=C);assert len(B)==8,'We should have eight messages in the response.';A=A[-1];assert H in A,f"The response of {D.__name__} should contain the delivery date.";F=Message(role=_B,text='How many Olea Tables can I find in store 17?');B+=[F];A=G.generate_text(model_name=E,messages=B,toolset=C);assert len(A)==3,'The response should contain 3 messages.';A=A[-1];assert'42'in A,f"The response of {D.__name__} should contain the number of tables.";print(_A)
		for L in B:print(L)
	def test_api_text_generation_with_thinking(I,api_class):
		A=api_class;E=A();B=E.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Thinking])
		if B is _C:pytest.skip(f"{A.__name__} does not support text generation with thinking. Skipping tests.")
		print(_A+B)
		if B is _C:print(f"{A.__name__} does not support thinking. Skipping test.");pytest.skip(f"{A.__name__} does not support thinking. Skipping test.")
		F=[Message(role=_E,text='You are a helpful assistant that helps with any task.'),Message(role=_B,text='What is the integral of x^2 from 0 to 1?')];C=E.generate_text(model_name=B,messages=F);C=C[0];print(_A+str(C));assert C is not _C,'No response received'
		if not(A.__name__=='OpenAIApi'or A.__name__=='CombinedApi'):G=[A for A in C if A.has_type(Text)and A.is_thinking()];assert len(G)>0,'No thinking blocks found in the response'
		H=F+[Message(role=_B,text='Wait, are you sure?')];D=E.generate_text(model_name=B,messages=H);D=D[-1];print(_A+str(D));assert D is not _C,'No follow-up response received'
```

==========
Prog. Lang. Code File: test_callback_manager.py
Size: 6.06 kilobytes
==========

test_callback_manager.py
```py
_F='Hello, world!'
_E='on_text_streaming'
_D='on_text_generation'
_C='on_best_model_selected'
_B='on_model_list'
_A='on_availability_check'
from typing import Any,List,Sequence
import pytest
from litemind.agent.messages.message import Message
from litemind.apis.callbacks.base_callbacks import BaseCallbacks
from litemind.apis.callbacks.callback_manager import CallbackManager
class MockCallback(BaseCallbacks):
	def __init__(A):A.called_methods=[]
	def on_availability_check(A,available):A.called_methods.append(_A)
	def on_model_list(A,models,**B):A.called_methods.append(_B)
	def on_best_model_selected(A,model_name,**B):A.called_methods.append(_C)
	def on_text_generation(A,messages,response,**B):A.called_methods.append(_D)
	def on_text_streaming(A,fragment,**B):A.called_methods.append(_E)
	def on_audio_transcription(A,audio_uri,transcription,**B):A.called_methods.append('on_audio_transcription')
	def on_document_conversion(A,document_uri,markdown,**B):A.called_methods.append('on_document_conversion')
	def on_video_conversion(A,video_uri,images,audio,**B):A.called_methods.append('on_video_conversion')
	def on_audio_generation(A,text,audio_uri,**B):A.called_methods.append('on_audio_generation')
	def on_image_generation(A,prompt,image,**B):A.called_methods.append('on_image_generation')
	def on_text_embedding(A,texts,embeddings,**B):A.called_methods.append('on_text_embedding')
	def on_image_embedding(A,image_uris,embeddings,**B):A.called_methods.append('on_image_embedding')
	def on_audio_embedding(A,audio_uris,embeddings,**B):A.called_methods.append('on_audio_embedding')
	def on_video_embedding(A,video_uris,embeddings,**B):A.called_methods.append('on_video_embedding')
	def on_image_description(A,image_uri,description,**B):A.called_methods.append('on_image_description')
	def on_audio_description(A,audio_uri,description,**B):A.called_methods.append('on_audio_description')
	def on_video_description(A,video_uri,description,**B):A.called_methods.append('on_video_description')
@pytest.fixture
def callback_manager():return CallbackManager()
@pytest.fixture
def mock_callback():return MockCallback()
def test_add_and_remove_callback(callback_manager,mock_callback):B=mock_callback;A=callback_manager;A.add_callback(B);assert len(A)==1;assert B in A;A.remove_callback(B);assert len(A)==0;assert B not in A
def test_on_availability_check(callback_manager,mock_callback):B=mock_callback;A=callback_manager;A.add_callback(B);A.on_availability_check(True);assert _A in B.called_methods
def test_on_model_list(callback_manager,mock_callback):B=mock_callback;A=callback_manager;A.add_callback(B);A.on_model_list(['model1','model2']);assert _B in B.called_methods
def test_on_best_model_selected(callback_manager,mock_callback):B=mock_callback;A=callback_manager;A.add_callback(B);A.on_best_model_selected('best_model');assert _C in B.called_methods
def test_on_text_generation(callback_manager,mock_callback):B=mock_callback;A=callback_manager;A.add_callback(B);C=Message(role='user',text=_F);D=Message(role='assistant',text='Hello you!');A.on_text_generation([C],D);assert _D in B.called_methods
def test_on_text_streaming(callback_manager,mock_callback):B=mock_callback;A=callback_manager;A.add_callback(B);A.on_text_streaming(_F);assert _E in B.called_methods
```

==========
Prog. Lang. Code File: test_callbacks_with_api.py
Size: 15.12 kilobytes
==========

test_callbacks_with_api.py
```py
_W='flying.mp4'
_V='cat.jpg'
_U='What is the meaning of life?'
_T='You are an omniscient all-knowing being called Ohmm'
_S='system'
_R='on_video_description'
_Q='on_audio_description'
_P='on_image_description'
_O='on_video_embedding'
_N='on_audio_embedding'
_M='on_image_embedding'
_L='on_text_embedding'
_K='on_image_generation'
_J='on_audio_generation'
_I='on_audio_transcription'
_H='on_text_streaming'
_G='on_text_generation'
_F='on_best_model_selected'
_E='on_model_list'
_D='on_availability_check'
_C='harvard.wav'
_B=None
_A='api_class'
from typing import Any,List,Sequence
import pytest
from litemind import API_IMPLEMENTATIONS
from litemind.agent.messages.message import Message
from litemind.apis.base_api import BaseApi
from litemind.apis.callbacks.base_callbacks import BaseCallbacks
from litemind.apis.callbacks.callback_manager import CallbackManager
from litemind.apis.model_features import ModelFeatures
from litemind.ressources.media_resources import MediaResources
class MockCallback(BaseCallbacks):
	def __init__(A):A.called_methods=[];A.call_parameters_dump=''
	def on_availability_check(A,available):A.called_methods.append(_D);A.call_parameters_dump+=f"available,"
	def on_model_list(A,models,**B):A.called_methods.append(_E);A.call_parameters_dump+=f"models=`{models}`,"
	def on_best_model_selected(A,model_name,**B):A.called_methods.append(_F);A.call_parameters_dump+=f"model_name=`{model_name}`,"
	def on_text_generation(A,messages,response,**B):A.called_methods.append(_G);A.call_parameters_dump+=f"messages=`{messages}`, response={response}, kwargs={B},"
	def on_text_streaming(A,fragment,**B):A.called_methods.append(_H);A.call_parameters_dump+=f"fragment=`{fragment}`, kwargs={B},"
	def on_audio_transcription(A,audio_uri,transcription,**B):A.called_methods.append(_I);A.call_parameters_dump+=f"audio_uri=`{audio_uri}`, transcription={transcription}, kwargs={B},"
	def on_document_conversion(A,document_uri,markdown,**B):A.called_methods.append('on_document_conversion');A.call_parameters_dump+=f"document_uri=`{document_uri}`, markdown={markdown},"
	def on_video_conversion(A,video_uri,images,audio,**B):A.called_methods.append('on_video_conversion');A.call_parameters_dump+=f"video_uri=`{video_uri}`, images={images}, audio={audio},"
	def on_audio_generation(A,text,audio_uri,**B):A.called_methods.append(_J);A.call_parameters_dump+=f"text=`{text}`, audio_uri={audio_uri},"
	def on_image_generation(A,prompt,image,**B):A.called_methods.append(_K);A.call_parameters_dump+=f"prompt=`{prompt}`, image={image},"
	def on_text_embedding(A,texts,embeddings,**B):A.called_methods.append(_L);A.call_parameters_dump+=f"texts=`{texts}`, embeddings={embeddings},"
	def on_image_embedding(A,image_uris,embeddings,**B):A.called_methods.append(_M);A.call_parameters_dump+=f"image_uris=`{image_uris}`, embeddings={embeddings},"
	def on_audio_embedding(A,audio_uris,embeddings,**B):A.called_methods.append(_N);A.call_parameters_dump+=f"audio_uris=`{audio_uris}`, embeddings={embeddings},"
	def on_video_embedding(A,video_uris,embeddings,**B):A.called_methods.append(_O);A.call_parameters_dump+=f"video_uris=`{video_uris}`, embeddings={embeddings},"
	def on_image_description(A,image_uri,description,**B):A.called_methods.append(_P);A.call_parameters_dump+=f"image_uri=`{image_uri}`, description={description},"
	def on_audio_description(A,audio_uri,description,**B):A.called_methods.append(_Q);A.call_parameters_dump+=f"audio_uri=`{audio_uri}`, description={description},"
	def on_video_description(A,video_uri,description,**B):A.called_methods.append(_R);A.call_parameters_dump+=f"video_uri=`{video_uri}`, description={description},"
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_availability_check(api_class):A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);C.check_availability_and_credentials();assert _D in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_model_list(api_class):A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);C.list_models();assert _E in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_best_model_selected(api_class):A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);C.get_best_model();assert _F in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_audio_transcription(api_class):
	A=api_class;B=CallbackManager();C=MockCallback();B.add_callback(C);D=A(callback_manager=B);E=D.get_best_model(ModelFeatures.AudioTranscription)
	if E is _B:pytest.skip(f"{A.__name__} does not support audio transcription. Skipping audio transcription tests.")
	F=MediaResources.get_local_test_audio_uri(_C);D.transcribe_audio(F,model_name=E);assert _I in C.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_text_generation(api_class):
	A=api_class;B=CallbackManager();C=MockCallback();B.add_callback(C);D=A(callback_manager=B);E=D.get_best_model([ModelFeatures.TextGeneration])
	if E is _B:pytest.skip(f"{A.__name__} does not support text generation. Skipping text generation tests.")
	F=[Message(role=_S,text=_T),Message(role='user',text=_U)];D.generate_text(model_name=E,messages=F);assert _G in C.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_text_generation_streaming(api_class):
	B=api_class;C=CallbackManager();A=MockCallback();C.add_callback(A);D=B(callback_manager=C);E=D.get_best_model([ModelFeatures.TextGeneration])
	if E is _B:pytest.skip(f"{B.__name__} does not support text generation. Skipping text generation tests.")
	F=[Message(role=_S,text=_T),Message(role='user',text=_U)];G=D.generate_text(model_name=E,messages=F);print(str(G));assert _H in A.called_methods;assert'fragment='in A.call_parameters_dump
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_audio_generation(api_class):
	A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);D=C.get_best_model(ModelFeatures.AudioGeneration)
	if D:C.generate_audio('Who is the president of the USA?');assert _J in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_image_generation(api_class):
	A=api_class;B=CallbackManager();C=MockCallback();B.add_callback(C);D=A(callback_manager=B);E=D.get_best_model(ModelFeatures.ImageGeneration)
	if E is _B:pytest.skip(f"{A.__name__} does not support image generation. Skipping image generation tests.")
	print(f"Image generation model name: {E}");D.generate_image('A cute fluffy cat.');assert _K in C.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_text_embedding(api_class):
	A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);D=C.get_best_model(ModelFeatures.TextEmbeddings)
	if D:C.embed_texts(texts=['Cat','Dog'],model_name=D);assert _L in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_image_embedding(api_class):
	A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);D=C.get_best_model(ModelFeatures.ImageEmbeddings)
	if D:E=MediaResources.get_local_test_image_uri(_V);F=MediaResources.get_local_test_image_uri('panda.jpg');C.embed_images(image_uris=[E,F],model_name=D);assert _M in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_audio_embedding(api_class):
	A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);D=C.get_best_model(ModelFeatures.AudioEmbeddings)
	if D:E=MediaResources.get_local_test_audio_uri(_C);F=MediaResources.get_local_test_audio_uri('preamble.wav');C.embed_audios(audio_uris=[E,F],model_name=D);assert _N in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_video_embedding(api_class):
	A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);D=C.get_best_model(ModelFeatures.VideoEmbeddings)
	if D:E=MediaResources.get_local_test_video_uri(_W);F=MediaResources.get_local_test_video_uri('lunar_park.mp4');C.embed_videos(video_uris=[E,F],model_name=D);assert _O in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_image_description(api_class):
	A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);D=C.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Image])
	if D:E=MediaResources.get_local_test_image_uri(_V);C.describe_image(E,model_name=D);assert _P in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_audio_description(api_class):
	A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);D=C.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Audio])
	if D:E=MediaResources.get_local_test_audio_uri(_C);C.describe_audio(E,model_name=D);assert _Q in B.called_methods
@pytest.mark.parametrize(_A,API_IMPLEMENTATIONS)
def test_video_description(api_class):
	A=CallbackManager();B=MockCallback();A.add_callback(B);C=api_class(callback_manager=A);D=C.get_best_model([ModelFeatures.TextGeneration,ModelFeatures.Video])
	if D:E=MediaResources.get_local_test_video_uri(_W);C.describe_video(E,model_name=D);assert _R in B.called_methods
```

==========
Prog. Lang. Code File: test_feature_scanner.py
Size: 12.70 kilobytes
==========

test_feature_scanner.py
```py
_C='modelB'
_B='modelA'
_A=None
import tempfile
from datetime import datetime
import pytest
from litemind import AnthropicApi,GeminiApi,OpenAIApi
from litemind.apis.base_api import BaseApi,ModelFeatures
from litemind.apis.feature_scanner import ModelFeatureScanner
class DummyApi(BaseApi):
	def __init__(A,*B,**C):super().__init__();A._models=[_B,_C];A._features={_B:[ModelFeatures.TextGeneration,ModelFeatures.ImageGeneration],_C:[ModelFeatures.TextGeneration,ModelFeatures.AudioGeneration,ModelFeatures.TextEmbeddings]}
	def check_availability_and_credentials(A,api_key=_A):return True
	def list_models(A,features=_A,non_features=_A,media_type=_A):return A._models
	def get_best_model(A,features=_A,non_features=_A,media_types=_A,exclusion_filters=_A):return A._models[0]
	def has_model_support_for(C,features,media_types=_A,model_name=_A):
		B=model_name;A=features
		if not B:B=C._models[0]
		A=ModelFeatures.normalise(A);return all(A in C._features[B]for A in A)
	def get_model_features(A,model_name):return A._features[model_name]
	def max_num_input_tokens(A,model_name=_A):return 4096
	def max_num_output_tokens(A,model_name=_A):return 1024
	def count_tokens(A,text,model_name=_A):return len(text.split())
	def generate_text(A,messages,model_name=_A,**B):return['hello world']
	def generate_audio(A,text,voice=_A,audio_format=_A,model_name=_A,**B):return'audio_uri'
	def generate_image(B,positive_prompt,negative_prompt=_A,model_name=_A,**C):from PIL import Image as A;return A.new('RGB',(1,1),color='white')
	def generate_video(A,description,model_name=_A,**B):return'video_uri'
	def embed_texts(A,texts,model_name=_A,dimensions=512,**B):return[[.1]*dimensions for A in texts]
	def embed_images(A,image_uris,model_name=_A,dimensions=512,**B):return[[.2]*dimensions for A in image_uris]
	def embed_audios(A,audio_uris,model_name=_A,dimensions=512,**B):return[[.3]*dimensions for A in audio_uris]
	def embed_videos(A,video_uris,model_name=_A,dimensions=512,**B):return[[.4]*dimensions for A in video_uris]
	def embed_documents(A,document_uris,model_name=_A,dimensions=512,**B):return[[.5]*dimensions for A in document_uris]
	def transcribe_audio(A,audio_uri,model_name=_A,**B):return'transcription'
	def describe_image(A,image_uri,**B):return'robot in the sky'
	def describe_audio(A,audio_uri,**B):return'smell of ham'
	def describe_video(A,video_uri,**B):return'roller coaster in amusement park'
	def describe_document(A,document_uri,**B):return'noise2self paper'
@pytest.fixture
def scanner():return ModelFeatureScanner(print_exception_stacktraces=True)
def test_scan_apis_and_query(scanner):A=scanner;A.scan_apis([DummyApi]);assert DummyApi in A.scan_results;assert set(A.scan_results[DummyApi].keys())=={_B,_C};B=A.get_supported_features(DummyApi,_B);C=A.get_supported_features(DummyApi,_C);assert ModelFeatures.TextGeneration in B;assert ModelFeatures.ImageGeneration in B;assert ModelFeatures.AudioGeneration in C;assert A.supports_feature(DummyApi,_B,ModelFeatures.TextGeneration);assert A.supports_feature(DummyApi,_B,ModelFeatures.AudioGeneration);assert A.supports_feature(DummyApi,_C,ModelFeatures.AudioGeneration)
def test_save_and_load_results(scanner):
	A=scanner;A.scan_apis([DummyApi])
	with tempfile.TemporaryDirectory()as B:C=A.save_results(folder=B);assert len(C)==2;A.scan_results={};A.load_results(folder=B);assert DummyApi in A.scan_results;assert _B in A.scan_results[DummyApi];assert A.supports_feature(DummyApi,_B,ModelFeatures.TextGeneration)
def test_no_yaml_files(tmp_path):A=ModelFeatureScanner();A.load_results(folder=str(tmp_path));assert A.scan_results=={}
def test_negative_feature_detection(scanner):
	A=scanner;A.scan_apis([DummyApi]);C=A.get_supported_features(DummyApi,_B);D=A.get_supported_features(DummyApi,_C);E=[ModelFeatures.StructuredTextGeneration,ModelFeatures.Thinking,ModelFeatures.Image,ModelFeatures.Audio,ModelFeatures.Video,ModelFeatures.Document,ModelFeatures.Tools,ModelFeatures.AudioTranscription,ModelFeatures.ImageConversion,ModelFeatures.AudioConversion,ModelFeatures.VideoConversion,ModelFeatures.DocumentConversion]
	for B in E:assert B not in C;assert B not in D
def test_query_nonexistent_api_or_model(scanner):
	A=scanner;A.scan_apis([DummyApi])
	class B:0
	assert A.get_supported_features(B,_B)==[];assert A.get_supported_features(DummyApi,'not_a_model')==[];assert not A.supports_feature(DummyApi,_B,ModelFeatures.Tools)
def test_full_feature_set_for_models(scanner):A=scanner;A.scan_apis([DummyApi]);B=set(A.get_supported_features(DummyApi,_B));C=set(A.get_supported_features(DummyApi,_C));F={ModelFeatures.TextGeneration,ModelFeatures.ImageGeneration,ModelFeatures.AudioGeneration,ModelFeatures.VideoGeneration,ModelFeatures.TextEmbeddings,ModelFeatures.ImageEmbeddings,ModelFeatures.AudioEmbeddings,ModelFeatures.VideoEmbeddings,ModelFeatures.DocumentEmbeddings};D=F.copy();E=F.copy();G={A for A in ModelFeatures};assert B==D,f"Mismatch for modelA. Expected: {D-B}, Got_Extra: {B-D}";assert C==E,f"Mismatch for modelB. Expected: {E-C}, Got_Extra: {C-E}"
def test_persistence_integrity(scanner):
	A=scanner;A.scan_apis([DummyApi]);C={A:{A:dict(B)for(A,B)in B.items()}for(A,B)in A.scan_results.items()}
	with tempfile.TemporaryDirectory()as B:A.save_results(folder=B);A.scan_results={};A.load_results(folder=B);D={A:{A:dict(B)for(A,B)in B.items()}for(A,B)in A.scan_results.items()};assert C==D
def test_generate_markdown_report(scanner):
	G='## API: DummyApi';F='# Model Feature Scan Report';B=scanner;B.scan_apis([DummyApi]);A=B.generate_markdown_report();print(A);assert isinstance(A,str);assert F in A;assert f"_Report generated on: {datetime.now().date().isoformat()}"in A;assert G in A;assert'### API Summary'in A;assert'*   **Total Models Scanned:** 2'in A;assert'### Model Details'in A;assert'#### Model: modelA'in A;assert'#### Model: modelB'in A;assert'✅ TextGeneration'in A;assert'❌ StructuredTextGeneration'in A;H=ModelFeatureScanner();I=H.generate_markdown_report();assert'No scan results available'in I;assert'**Average Supported Features per Model:**'in A;assert'#### Feature Support Across Models:'in A;assert'TextGeneration: 100.0% (2/2)'in A;J='Model: modelA \\(\\d+/\\d+ features supported\\)';import re;assert re.search(J,A)is not _A
	with tempfile.TemporaryDirectory()as K:
		C=B.save_results(folder=K);assert len(C)>=2;D=next((A for A in C if A.endswith('.md')),_A);assert D is not _A
		with open(D,'r')as L:E=L.read();assert F in E;assert G in E
def test_scan_openai_for_debug(scanner):A=scanner;B='o3-mini-high';A.scan_apis([OpenAIApi],model_names=[B]);assert OpenAIApi in A.scan_results;assert len(A.scan_results[OpenAIApi])>0;C=A.get_supported_features(OpenAIApi,B);assert ModelFeatures.TextGeneration in C;assert ModelFeatures.Image not in C
def test_scan_gemini_for_debug(scanner):A=scanner;B='models/gemini-1.5-pro';A.scan_apis([GeminiApi],model_names=[B]);assert GeminiApi in A.scan_results;assert len(A.scan_results[GeminiApi])>0;C=A.get_supported_features(GeminiApi,B);assert ModelFeatures.TextGeneration in C;assert ModelFeatures.Image in C
def test_scan_claude_for_debug(scanner):A=scanner;B='claude-opus-4-20250514';A.scan_apis([AnthropicApi],model_names=[B]);assert AnthropicApi in A.scan_results;assert len(A.scan_results[AnthropicApi])>0;C=A.get_supported_features(AnthropicApi,B);assert ModelFeatures.TextGeneration in C;assert ModelFeatures.Image in C
```

==========
Prog. Lang. Code File: test_feature_scanner_specifics.py
Size: 5.82 kilobytes
==========

test_feature_scanner_specifics.py
```py
from typing import Type
import pytest
from arbol import aprint,asection
from litemind import AnthropicApi,GeminiApi,OpenAIApi
from litemind.apis.base_api import BaseApi,ModelFeatures
from litemind.apis.feature_scanner import ModelFeatureScanner
@pytest.fixture
def scanner():return ModelFeatureScanner(print_exception_stacktraces=True)
def test_scan_openai_api_check_specifics(scanner):
	with asection('Available models in OpenAI API:'):
		for A in OpenAIApi().list_models():aprint(f"- {A}")
	B={'gpt-4.1':[ModelFeatures.TextGeneration,ModelFeatures.StructuredTextGeneration,ModelFeatures.Image,ModelFeatures.Tools,ModelFeatures.WebSearchTool,ModelFeatures.MCPTool],'gpt-4o':[ModelFeatures.TextGeneration,ModelFeatures.StructuredTextGeneration,ModelFeatures.Image,ModelFeatures.Tools],'o1-medium':[ModelFeatures.TextGeneration,ModelFeatures.StructuredTextGeneration,ModelFeatures.Image,ModelFeatures.Tools],'o3-medium':[ModelFeatures.TextGeneration,ModelFeatures.StructuredTextGeneration,ModelFeatures.Image,ModelFeatures.Tools]};_check_features(scanner,OpenAIApi,B)
def test_scan_anthropic_api_check_specifics(scanner):
	with asection('Available models in Anthropic API:'):
		for A in AnthropicApi().list_models():aprint(f"- {A}")
	B={'claude-opus-4-20250514':[ModelFeatures.WebSearchTool,ModelFeatures.MCPTool,ModelFeatures.TextGeneration,ModelFeatures.StructuredTextGeneration,ModelFeatures.Image,ModelFeatures.Tools]};_check_features(scanner,AnthropicApi,B)
def test_scan_gemini_api_check_specifics(scanner):
	with asection('Available models in Gemini API:'):
		for A in GeminiApi().list_models():aprint(f"- {A}")
	B={'models/gemini-1.5-pro':[ModelFeatures.TextGeneration,ModelFeatures.StructuredTextGeneration,ModelFeatures.Image,ModelFeatures.Tools],'models/gemini-1.5-flash':[ModelFeatures.TextGeneration,ModelFeatures.StructuredTextGeneration,ModelFeatures.Image]};_check_features(scanner,GeminiApi,B)
def _check_features(scanner,api,model_feature_map):
	E=scanner;C=api
	for(B,F)in model_feature_map.items():
		if B not in C().list_models():pytest.skip(f"Model {B} not available in OpenAI API.")
		for A in F:
			if isinstance(A,tuple)and len(A)==1:A=A[0];D=E.test_feature(C,B,A);aprint(f"Result for {B} with feature {A}: {D}");assert not D,f"NOT expected feature {A} for model {B} from {C}";aprint
			else:D=E.test_feature(C,B,A);aprint(f"Result for {B} with feature {A}: {D}");assert D,f"Expected feature {A} for model {B} from {C}"
```




==========
Prog. Lang. Code File: media_base.py
Size: 1.07 kilobytes
==========

media_base.py
```py
from abc import ABC,abstractmethod
from typing import Any
class MediaBase(ABC):
	@abstractmethod
	def get_content(self):0
	@abstractmethod
	def to_message_block(self):0
	@abstractmethod
	def __str__(self):0
	@abstractmethod
	def __len__(self):0
```

==========
Prog. Lang. Code File: media_default.py
Size: 927 bytes
==========

media_default.py
```py
from litemind.media.media_base import MediaBase
from litemind.utils.pickle_serialisation import PickleSerializable
class MediaDefault(MediaBase,PickleSerializable):
	def __init__(A,**B):A.attributes=B
	def to_message_block(A):from litemind.agent.messages.message_block import MessageBlock as B;return B(A)
	def __str__(A):return str(A.get_content())
	def __len__(A):return len(str(A))
	def __contains__(A,item):return item in A.get_content()
	def __hash__(A):return hash(A.get_content())
	def __eq__(B,other):
		A=other
		if not isinstance(A,MediaDefault):return False
		return B.get_content()==A.get_content()
```

==========
Prog. Lang. Code File: media_uri.py
Size: 5.24 kilobytes
==========

media_uri.py
```py
_B='file://'
_A=None
import os
from abc import abstractmethod
from typing import Optional
from litemind.media.media_default import MediaDefault
from litemind.utils.get_media_type_from_uri import get_media_type_from_uri
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
from litemind.utils.read_file_and_convert_to_base64 import base64_to_data_uri,read_file_and_convert_to_base64
from litemind.utils.uri_utils import is_uri,is_valid_path
class MediaURI(MediaDefault):
	def __init__(B,uri,extension=_A,**D):
		C=extension;A=uri;super().__init__(**D)
		if not is_uri(A)or not is_valid_path(A):raise ValueError(f"Invalid URI or local file path: '{A}'.")
		if not is_uri(A):A=os.path.abspath(A);A=_B+A
		B.uri=A;B.extension=C.lower()if C else _A;B.local_path=_A
	def get_content(A):return A.uri
	def is_local(A):return A.uri.startswith(_B)or A.uri.startswith('/')
	def get_filename(A):return A.uri.split('/')[-1]if A.uri else _A
	def get_extension(A):return A.extension or A.uri.split('.')[-1].lower()
	def has_extension(B,extension):A=extension;A=A.lower();return A.lower()in B.get_extension()or B.uri.endswith(A)
	def get_media_type(A):return get_media_type_from_uri(A.uri)
	def get_mime_type(A,mime_prefix):return f"{mime_prefix}/{A.get_extension()}"
	def to_remote_or_data_uri(B):
		A=B.uri
		if A.startswith(_B):C=get_media_type_from_uri(A);D=A.replace(_B,'');E=read_file_and_convert_to_base64(D);A=base64_to_data_uri(E,C)
		return A
	def to_base64_data(A):
		if A.uri.startswith('data:'):return A.uri.split(',')[-1]
		B=uri_to_local_file_path(A.uri);C=read_file_and_convert_to_base64(B);return C
	def to_local_file_path(A):
		if A.local_path:return A.local_path
		A.local_path=uri_to_local_file_path(A.uri);return A.local_path
	@abstractmethod
	def load_from_uri(self):0
	def __str__(A):return A.uri
	def __repr__(A):return f"MediaURI({A.uri})"
	def __len__(A):return len(A.uri)
```




==========
Prog. Lang. Code File: media_converter.py
Size: 15.22 kilobytes
==========

media_converter.py
```py
_C=False
_B=None
_A=True
from typing import List,Optional,Sequence,Set,Type
from arbol import aprint
from litemind.agent.messages.message import Message
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_action import Action
from litemind.media.types.media_types import all_media_types
class MediaConverter:
	def __init__(A):A.media_converters=[]
	def add_default_converters(C,convert_audio=_A,convert_videos=_A,convert_documents=_A):
		B=convert_documents;A=[];from litemind.media.conversion.converters.code_converter import CodeConverter as D;from litemind.media.conversion.converters.document_converter_docling import is_docling_available as E;from litemind.media.conversion.converters.document_converter_pymupdf import is_pymupdf_available as F;from litemind.media.conversion.converters.document_converter_txt import DocumentConverterTxt as G;from litemind.media.conversion.converters.file_converter import FileConverter as H;from litemind.media.conversion.converters.json_converter import JsonConverter as I;from litemind.media.conversion.converters.ndimage_converter import NdImageConverter as J;from litemind.media.conversion.converters.object_converter import ObjectConverter as K;from litemind.media.conversion.converters.table_converter import TableConverter as L;from litemind.utils.ffmpeg_utils import is_ffmpeg_available as M;from litemind.utils.whisper_transcribe_audio import is_local_whisper_available as N;A.append(J());A.append(L());A.append(K());A.append(D());A.append(I())
		if convert_videos and M():from litemind.media.conversion.converters.video_converter_ffmpeg import VideoConverterFfmpeg as O;A.append(O())
		if B and F():from litemind.media.conversion.converters.document_converter_pymupdf import DocumentConverterPymupdf as P;A.append(P())
		if B and E():from litemind.media.conversion.converters.document_converter_docling import DocumentConverterDocling as Q;A.append(Q())
		if convert_audio and N():from litemind.media.conversion.converters.audio_converter_whisper_local import AudioConverterWhisperLocal as R;A.append(R())
		if B:A.append(G())
		A.append(H());C.media_converters.extend(A);return A
	def add_media_converter(A,media_converter,highest_priority=_C):
		B=media_converter
		if highest_priority:A.media_converters.insert(0,B)
		else:A.media_converters.append(B)
	def remove_media_converter(A,media_converter):A.media_converters.remove(media_converter)
	def can_convert_within(G,source_media_type,allowed_media_types):
		I=allowed_media_types;H=source_media_type
		if not G.media_converters:return H in I
		A={}
		for J in G.media_converters:
			for(C,K)in J.rule():
				if C not in A:A[C]=set()
				A[C].update(K)
		D=[H];E=set()
		while D:
			B=D.pop(0)
			if B in E:continue
			E.add(B)
			if B in A:
				for F in A[B]:
					if F not in I:return _C
					if F not in E:D.append(F)
		return _A
	def get_convertible_media_types(C,allowed_media_types):
		A=set()
		for B in all_media_types():
			if C.can_convert_within(B,allowed_media_types):A.add(B)
		return A
	def convert(G,messages,allowed_media_types,exclude_extensions=_B,recursive=_A):
		H=exclude_extensions;D=allowed_media_types;E=[];I=_C
		for J in messages:
			B=Message(role=J.role)
			for C in J.blocks:
				N=C.attributes;A=C.media;K=getattr(A,'extension',_B)
				if any(isinstance(A,B)for B in D):B.append_block(C)
				elif K is not _B and H is not _B and K in H:B.append_block(C)
				else:
					F=_B
					for L in G.media_converters:
						try:
							if L.can_convert(A):F=L.convert(A);break
						except Exception as M:aprint('Error during conversion:',M);B.append_text(f"Could not convert {type(A)} to {type(D[0])} because of error {M}.");import traceback as O;O.print_exc()
					if F is not _B:
						I=_A
						for A in F:B.append_block(MessageBlock(media=A,attributes=N))
					else:
						B.append_block(C)
						if not any(isinstance(A,B)for B in D)and not isinstance(A,Action):aprint(f"Warning: No converter found for non-allowed media of type: {type(A)}")
			E.append(B)
		if recursive and I:return G.convert(E,D,recursive=_A)
		return E
```




==========
Prog. Lang. Code File: audio_converter_whisper_local.py
Size: 1.83 kilobytes
==========

audio_converter_whisper_local.py
```py
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_text import Text
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
from litemind.utils.whisper_transcribe_audio import is_local_whisper_available,transcribe_audio_with_local_whisper
class AudioConverterWhisperLocal(BaseConverter):
	def rule(A):return[(Audio,[Text])]
	def can_convert(B,media):A=media;return A is not None and isinstance(A,Audio)
	def convert(G,media):
		A=media
		if not isinstance(A,Audio):raise ValueError(f"Expected Video media, got {type(A)}")
		if not is_local_whisper_available():raise RuntimeError('Whisper is not available. Please install Whisper to use this converter.')
		C=A.uri;D=uri_to_local_file_path(C);E=A.get_info_markdown();F=transcribe_audio_with_local_whisper(D);B=[];B.append(Text(E+'\n\nThe following is the transcription of the audio:\n'+F));return B
```

==========
Prog. Lang. Code File: base_converter.py
Size: 1.74 kilobytes
==========

base_converter.py
```py
from abc import ABC,abstractmethod
from typing import List,Tuple,Type
from litemind.media.media_base import MediaBase
class BaseConverter(ABC):
	@abstractmethod
	def rule(self):0
	@abstractmethod
	def can_convert(self,media):0
	@abstractmethod
	def convert(self,media):0
```

==========
Prog. Lang. Code File: code_converter.py
Size: 897 bytes
==========

code_converter.py
```py
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_code import Code
from litemind.media.types.media_text import Text
class CodeConverter(BaseConverter):
	def rule(A):return[(Code,[Text])]
	def can_convert(B,media):A=media;return A is not None and isinstance(A,Code)
	def convert(B,media):
		A=media
		if not isinstance(A,Code):raise ValueError(f"Expected Json media, got {type(A)}")
		return[A.to_markdown_text_media()]
```

==========
Prog. Lang. Code File: document_converter_docling.py
Size: 6.37 kilobytes
==========

document_converter_docling.py
```py
_C=False
_B=None
_A=True
from functools import lru_cache
from typing import List,Optional,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_document import Document
from litemind.media.types.media_text import Text
from litemind.utils.file_types.file_types import classify_uri
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class DocumentConverterDocling(BaseConverter):
	def rule(A):return[(Document,[Text])]
	def can_convert(G,media):
		F='pdf';E='text';D='office';C=media
		if C is _B:return _C
		if not isinstance(C,Document):return _C
		A=C.get_extension();B=classify_uri(C.uri)
		if F in B or F in A:return _A
		if D in B and'docx'in A:return _A
		if D in B and'xlsx'in A:return _A
		if D in B and'pptx'in A:return _A
		if E in B and'md'in A:return _A
		if E in B and'adoc'in A:return _A
		if E in B and('html'in A or'xhtml'in A):return _A
		return _C
	def convert(G,media):
		B=media
		if not isinstance(B,Document):raise ValueError(f"Expected Document media, got {type(B)}")
		A=convert_to_markdown(B.uri);C=[];E=len(A);F=DocumentConverterDocling.get_preamble(B,E);C.append(Text(F))
		if len(A)==1:C.append(Text(A[0]))
		else:
			for D in A:D=f"---\nPage {A.index(D)+1} of {len(A)}\n---\n{D}";C.append(Text(D))
		return C
	@staticmethod
	def get_preamble(media,num_pages=_B):
		B=media;A=num_pages;C=B.get_filename();D=B.get_extension();E=classify_uri(B.uri)
		if A is _B:A='unknown'
		else:A=int(A)
		F=f"""
Document: {C}
Type: {E}
Extension: {D}
Number of pages: {A}
 """;return F
@lru_cache()
def is_docling_available():
	try:import importlib.util;return importlib.util.find_spec('docling')is not _B
	except ImportError:return _C
def initialize_docling_converter():from docling.datamodel.base_models import InputFormat as B;from docling.datamodel.pipeline_options import PaginatedPipelineOptions as D,PdfPipelineOptions as E;from docling.document_converter import DocumentConverter as F,PdfFormatOption as G,WordFormatOption as H;A=E();A.do_ocr=_A;A.do_table_structure=_A;A.generate_page_images=_A;C=D();C.generate_page_images=_A;I=F(format_options={B.PDF:G(pipeline_options=A),B.DOCX:H(pipeline_options=C)});return I
__default_docling_converter=initialize_docling_converter()
def convert_to_markdown(document_uri):
	if not is_docling_available():raise ImportError('docling is not available. Please install it to use this function.')
	D=uri_to_local_file_path(document_uri);E=__default_docling_converter.convert(D);A=E.document;B=[]
	if A.pages:
		for(G,F)in enumerate(A.pages):C=F.export_to_markdown();B.append(C)
	else:C=A.export_to_markdown();B.append(C)
	return B
```

==========
Prog. Lang. Code File: document_converter_pymupdf.py
Size: 5.49 kilobytes
==========

document_converter_pymupdf.py
```py
_C='pdf'
_B=False
_A=None
from functools import lru_cache
from typing import List,Tuple,Type
from arbol import aprint
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.conversion.converters.document_converter_docling import DocumentConverterDocling
from litemind.media.media_base import MediaBase
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_text import Text
from litemind.utils.file_types.file_types import classify_uri
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class DocumentConverterPymupdf(BaseConverter):
	def rule(A):return[(Document,[Text,Image])]
	def can_convert(I,media):
		H='svg';G='fb2';F='xps';E='epub';D=media;C=True
		if D is _A:return _B
		if not isinstance(D,Document):return _B
		A=D.get_extension();B=classify_uri(D.uri)
		if _C in B or _C in A:return C
		if E in B or E in A:return C
		if F in B or F in A:return C
		if G in B or G in A:return C
		if H in B or H in A:return C
		return _B
	def convert(G,media):
		A=media
		if not isinstance(A,Document):raise ValueError(f"Expected Document media, got {type(A)}")
		E=extract_text_and_image_from_document(A.uri);B=[];F=DocumentConverterDocling.get_preamble(A,_A);B.append(Text(F))
		for(C,D)in E:
			if C is _A:continue
			B.append(C)
			if D is not _A:B.append(D)
		return B
@lru_cache()
def is_pymupdf_available():
	try:import importlib.util;return importlib.util.find_spec('pymupdf4llm')is not _A and importlib.util.find_spec('pymupdf')is not _A
	except ImportError:return _B
def extract_text_and_image_from_document(document_uri,dpi=300):
	if not is_pymupdf_available():raise ImportError('pymupdf is not available. Please install it to use this function.')
	B=uri_to_local_file_path(document_uri);G=classify_uri(B)
	if not G==_C:raise ValueError(f"Unsupported file type: {G}. Only PDF files are supported.")
	try:
		import fitz as H;from pymupdf import Pixmap;C=H.open(B);D=[];I=dpi/72.;K=H.Matrix(I,I)
		for A in range(len(C)):
			try:J=C.load_page(A);E=J.get_text('text');E=f"Page {A+1}:\n```text\n{E}\n```";L=Text(E);M=J.get_pixmap(matrix=K);N=M.pil_image();O=Image.from_PIL_image(N);D.append((L,O))
			except Exception as F:aprint(f"Error processing page {A}: {F}");D.append((f"Page {A} could not be processed.",_A));continue
		C.close()
	except Exception as F:raise RuntimeError(f"Failed to open document '{B}': {F}")
	return D
```

==========
Prog. Lang. Code File: document_converter_python_minify.py
Size: 5.76 kilobytes
==========

document_converter_python_minify.py
```py
_C=None
_B=False
_A=True
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_document import Document
from litemind.media.types.media_text import Text
from litemind.utils.file_types.file_types import classify_uri
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class DocumentConverterPythonMinify(BaseConverter):
	def __init__(C,remove_pass=_A,remove_literal_statements=_A,combine_imports=_A,hoist_literals=_A,rename_locals=_A,preserve_locals=_C,rename_globals=_B,preserve_globals=_C,remove_object_base=_A,convert_posargs_to_args=_A,preserve_shebang=_A,remove_asserts=_B,remove_debug=_B,remove_explicit_return_none=_A,remove_builtin_exception_brackets=_A,constant_folding=_A):B=preserve_globals;A=preserve_locals;super().__init__();C.kwargs_minify={'remove_pass':remove_pass,'remove_literal_statements':remove_literal_statements,'combine_imports':combine_imports,'hoist_literals':hoist_literals,'rename_locals':rename_locals,'preserve_locals':A if A is not _C else[],'rename_globals':rename_globals,'preserve_globals':B if B is not _C else[],'remove_object_base':remove_object_base,'convert_posargs_to_args':convert_posargs_to_args,'preserve_shebang':preserve_shebang,'remove_asserts':remove_asserts,'remove_debug':remove_debug,'remove_explicit_return_none':remove_explicit_return_none,'remove_builtin_exception_brackets':remove_builtin_exception_brackets,'constant_folding':constant_folding}
	def rule(A):return[(Document,[Text])]
	def can_convert(C,media):
		A=media
		if A is _C:return _B
		if not isinstance(A,Document):return _B
		B=classify_uri(A.uri)
		if B not in{'code'}and not A.get_extension()in{'py'}:return _B
		return _A
	def convert(B,media):
		A=media
		if not isinstance(A,Document):raise ValueError(f"Expected Document media, got {type(A)}")
		C=A.get_filename();D=A.get_extension()
		if A.get_extension()!='py':raise ValueError(f"Expected a Python file, got {A.get_extension()}")
		E=uri_to_local_file_path(A.uri)
		with open(E,'r',encoding='utf-8')as F:G=F.read()
		from python_minifier import minify as H;I=H(G,**B.kwargs_minify);J=f"{C}\n```{D}\n{I}\n```";K=Text(J);return[K]
```

==========
Prog. Lang. Code File: document_converter_txt.py
Size: 2.03 kilobytes
==========

document_converter_txt.py
```py
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_document import Document
from litemind.media.types.media_text import Text
from litemind.utils.file_types.file_types import classify_uri
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class DocumentConverterTxt(BaseConverter):
	def rule(A):return[(Document,[Text])]
	def can_convert(D,media):
		B=False;A=media
		if A is None:return B
		if not isinstance(A,Document):return B
		C=classify_uri(A.uri)
		if C not in{'text','code','script'}:return B
		return True
	def convert(H,media):
		A=media
		if not isinstance(A,Document):raise ValueError(f"Expected Document media, got {type(A)}")
		C=uri_to_local_file_path(A.uri);D=A.get_filename();E=A.get_extension()
		with open(C,'r',encoding='utf-8')as F:B=F.read()
		B=f"{D}\n```{E}\n{B}\n```";G=Text(B);return[G]
```

==========
Prog. Lang. Code File: file_converter.py
Size: 990 bytes
==========

file_converter.py
```py
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_file import File
from litemind.media.types.media_text import Text
class FileConverter(BaseConverter):
	def rule(A):return[(File,[Text])]
	def can_convert(B,media):A=media;return A is not None and isinstance(A,File)
	def convert(C,media):
		A=media
		if not isinstance(A,File):raise ValueError(f"Expected File media, got {type(A)}")
		B=A;return[B.to_markdown_text_media()]
```

==========
Prog. Lang. Code File: json_converter.py
Size: 896 bytes
==========

json_converter.py
```py
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_json import Json
from litemind.media.types.media_text import Text
class JsonConverter(BaseConverter):
	def rule(A):return[(Json,[Text])]
	def can_convert(B,media):A=media;return A is not None and isinstance(A,Json)
	def convert(B,media):
		A=media
		if not isinstance(A,Json):raise ValueError(f"Expected Json media, got {type(A)}")
		return[A.to_markdown_text_media()]
```

==========
Prog. Lang. Code File: media_converter_delegated_callables.py
Size: 3.87 kilobytes
==========

media_converter_delegated_callables.py
```py
from typing import List,Tuple,Type
from litemind.apis.base_api import BaseApi
from litemind.apis.model_features import ModelFeatures
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
class MediaConverterApi(BaseConverter):
	def __init__(A,api):super().__init__();A.api=api
	def rule(B):
		A=[]
		if B.api.has_model_support_for(ModelFeatures.Image):A.append((Image,[Text]))
		if B.api.has_model_support_for(ModelFeatures.Audio):A.append((Audio,[Text]))
		if B.api.has_model_support_for(ModelFeatures.Video):A.append((Video,[Text]))
		if B.api.has_model_support_for(ModelFeatures.Document):A.append((Document,[Text]))
		return A
	def can_convert(A,media):
		C=True;B=media
		if isinstance(B,Image)and A.api.has_model_support_for(ModelFeatures.Image):return C
		elif isinstance(B,Audio)and A.api.has_model_support_for(ModelFeatures.Audio):return C
		elif isinstance(B,Video)and A.api.has_model_support_for(ModelFeatures.Video):return C
		elif isinstance(B,Document)and A.api.has_model_support_for(ModelFeatures.Document):return C
		else:return False
	def convert(B,media):
		A=media
		try:
			if isinstance(A,Image)and B.api.has_model_support_for(ModelFeatures.Image):C=B.api.describe_image(A.uri)
			elif isinstance(A,Audio)and B.api.has_model_support_for(ModelFeatures.Audio):C=B.api.describe_audio(A.uri)
			elif isinstance(A,Video)and B.api.has_model_support_for(ModelFeatures.Video):C=B.api.describe_video(A.uri)
			elif isinstance(A,Document)and B.api.has_model_support_for(ModelFeatures.Document):C=B.api.describe_document(A.uri)
			else:raise ValueError(f"Expected Image, Audio, Video, or Document media, got {type(A)}")
			D=Text(C);return[D]
		except Exception as F:import traceback as E;E.print_exc();return[A]
```

==========
Prog. Lang. Code File: ndimage_converter.py
Size: 1.04 kilobytes
==========

ndimage_converter.py
```py
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_image import Image
from litemind.media.types.media_ndimage import NdImage
from litemind.media.types.media_text import Text
class NdImageConverter(BaseConverter):
	def rule(A):return[(NdImage,[Text,Image])]
	def can_convert(B,media):A=media;return A is not None and isinstance(A,NdImage)
	def convert(B,media):
		A=media
		if not isinstance(A,NdImage):raise ValueError(f"Expected NdImage media, got {type(A)}")
		return A.to_text_and_2d_projection_medias()
```

==========
Prog. Lang. Code File: object_converter.py
Size: 1.01 kilobytes
==========

object_converter.py
```py
from typing import Any,List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_object import Object
from litemind.media.types.media_text import Text
class ObjectConverter(BaseConverter):
	def rule(A):return[(Object,[Text])]
	def can_convert(B,media):A=media;return A is not None and isinstance(A,Object)
	def convert(B,media):
		A=media
		if not isinstance(A,Object):raise ValueError(f"Expected Object media, got {type(A)}")
		return[A.to_markdown_text_media()]
```

==========
Prog. Lang. Code File: table_converter.py
Size: 906 bytes
==========

table_converter.py
```py
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
class TableConverter(BaseConverter):
	def rule(A):return[(Table,[Text])]
	def can_convert(B,media):A=media;return A is not None and isinstance(A,Table)
	def convert(B,media):
		A=media
		if not isinstance(A,Table):raise ValueError(f"Expected Table media, got {type(A)}")
		return[A.to_markdown_text_media()]
```

==========
Prog. Lang. Code File: video_converter_ffmpeg.py
Size: 4.51 kilobytes
==========

video_converter_ffmpeg.py
```py
import os,tempfile
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_image import Image
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
from litemind.utils.ffmpeg_utils import extract_frames_and_audio,get_video_info,is_ffmpeg_available
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class VideoConverterFfmpeg(BaseConverter):
	def rule(A):return[(Video,[Text,Image,Audio])]
	def can_convert(B,media):A=media;return A is not None and isinstance(A,Video)
	def convert(C,media):
		A=media
		if not isinstance(A,Video):raise ValueError(f"Expected Video media, got {type(A)}")
		B=convert_video_to_info_frames_and_audio(A);return B
def convert_video_to_info_frames_and_audio(media,frame_interval=1,key_frames=False):
	N='file://';M='resolution';H=key_frames;E=frame_interval;D=media
	if not is_ffmpeg_available():raise RuntimeError('FFmpeg is not available. Please install FFmpeg.')
	if not isinstance(D,Video):raise ValueError(f"Expected Video media, got {type(D)}")
	O=D.uri;F=uri_to_local_file_path(O);P=tempfile.mkdtemp();I,J=extract_frames_and_audio(input_video_path=F,output_dir=P,fps=E,use_keyframes=H);B=get_video_info(F);A='';A+=f"Video: {os.path.basename(F)}\n";A+=f"Video duration: {B["duration"]} seconds.\n";A+=f"Video resolution: {B[M][0]}x{B[M][1]}.\n";A+=f"Video codec: {B["codec"]}.\n";A+=f"Video bit rate: {B["bit_rate"]} bps.\n";A+=f"Video frame rate: {B["frame_rate"]} fps.\n";A+=f"Frames sampled every {E} seconds.\n"if not H else'Key frames extracted.\n';K=len(I);A+=f"the video content is provided below as a sequence of {K} image frames:\n";C=[];C.append(Text(A))
	for(L,Q)in enumerate(I):G=L*E;R=int(G//3600);S=int(G%3600//60);T=int(G%60);U=f"{R:02d}hr {S:02d}min {T:02d}sec";C.append(Text(f"Frame at {U} ({L}/ {K}): "));C.append(Image(N+Q))
	if J is not None:C.append(Text(f"The video's audio is provided separately below:\n"));C.append(Audio(N+J))
	return C
```




==========
Prog. Lang. Code File: test_media_converter_basics.py
Size: 12.27 kilobytes
==========

test_media_converter_basics.py
```py
_G='low_discrepancy_sequence.pdf'
_F='spreadsheet.csv'
_E='test'
_D='name'
_C='Hello world'
_B='value'
_A='user'
from litemind.agent.messages.message import Message
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.conversion.converters.table_converter import TableConverter
from litemind.media.conversion.media_converter import MediaConverter
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_json import Json
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.ressources.media_resources import MediaResources
class TestMessageConverter:
	def setup_method(A):A.converter=MediaConverter()
	def test_init(A):assert A.converter.media_converters==[]
	def test_add_default_converters(A):B=A.converter.add_default_converters();assert len(B)>0;assert len(A.converter.media_converters)>0;assert all(B in A.converter.media_converters for B in B)
	def test_add_remove_converter(A):B=TableConverter();A.converter.add_media_converter(B);assert len(A.converter.media_converters)>=1;assert B in A.converter.media_converters;A.converter.remove_media_converter(B);assert B not in A.converter.media_converters
	def test_convert_no_conversion_needed(D):E=Text(_C);F=Json({_D:_E,_B:42});B=Message(role=_A);B.append_block(MessageBlock(E));C=Message(role=_A);C.append_block(MessageBlock(F));G=[B,C];H=[Text,Json];A=D.converter.convert(G,H);assert len(A)==2;assert A[0]is not B;assert isinstance(A[0][0].media,Text);assert _C in A[0][0].media.text;assert A[1]is not C;assert isinstance(A[1][0].media,Json);assert A[1][0].media.json=={_D:_E,_B:42}
	def test_convert_with_default_converters(A):A.converter.add_default_converters();F=MediaResources.get_local_test_table_uri('trees.csv');G=Table(F);H=MediaResources.get_local_test_document_uri('timaeus.txt');I=Document(H);J=Json({_D:_E,_B:42});B=Message(role=_A);B.append_block(MessageBlock(G));C=Message(role=_A);C.append_block(MessageBlock(I));D=Message(role=_A);D.append_block(MessageBlock(J));K=[Text];E=A.converter.convert([B,C,D],K);assert len(E)==3;assert all(isinstance(A[0].media,Text)for A in E)
	def test_convert_multiple_media_in_message(C):D='Original text';C.converter.add_default_converters();E=MediaResources.get_local_test_document_uri('intracktive_preprint.pdf');F=Text(D);G=Document(E);H=Json({'data':_B});A=Message(role=_A);A.append_block(MessageBlock(F));A.append_block(MessageBlock(G));A.append_block(MessageBlock(H));I=[Text];B=C.converter.convert([A],I);assert len(B)==1;assert len(B[0].blocks)==13;assert all(isinstance(A.media,Text)or isinstance(A.media,Image)for A in B[0].blocks);assert B[0][0].media.text==D
	def test_convert_empty_messages(A):B=A.converter.convert([],[Text]);assert B==[]
	def test_convert_mixed_allowed_types(B):B.converter.add_default_converters();G=MediaResources.get_local_test_table_uri(_F);H=Table(G);I=MediaResources.get_local_test_document_uri(_G);J=Document(I);K=Json({_D:_E,_B:42});L=Text(_C);C=Message(role=_A);C.append_block(MessageBlock(H));D=Message(role=_A);D.append_block(MessageBlock(J));E=Message(role=_A);E.append_block(MessageBlock(K));F=Message(role=_A);F.append_block(MessageBlock(L));M=[Text,Json];A=B.converter.convert([C,D,E,F],M);assert len(A)==4;assert isinstance(A[0][0].media,Text);assert isinstance(A[1][0].media,Text);assert isinstance(A[2][0].media,Json);assert isinstance(A[3][0].media,Text);assert A[3][0].media.text==_C
	def test_complex_message_with_mixed_allowed_types(C):C.converter.add_default_converters();D=Text('Hello');E=Json({'data':123});F=MediaResources.get_local_test_table_uri(_F);G=Table(F);H=MediaResources.get_local_test_document_uri(_G);I=Document(H);B=Message(role=_A);B.append_block(MessageBlock(D));B.append_block(MessageBlock(E));B.append_block(MessageBlock(G));B.append_block(MessageBlock(I));J=[Text,Json];A=C.converter.convert([B],J);assert len(A)==1;assert len(A[0].blocks)==20;assert isinstance(A[0][0].media,Text);assert isinstance(A[0][1].media,Json);assert isinstance(A[0][2].media,Text);assert isinstance(A[0][3].media,Text);assert isinstance(A[0][4].media,Text);assert isinstance(A[0][5].media,Image);assert isinstance(A[0][6].media,Text);assert isinstance(A[0][7].media,Image)
	def test_all_media_types_conversion(E):I='key';H='Sample text';E.converter.add_default_converters();C=[];A=Message(role=_A);A.append_text(H);C.append(A);A=Message(role=_A);A.append_block(MessageBlock(Json({I:_B})));C.append(A);A=Message(role=_A);F=MediaResources.get_local_test_table_uri(_F);A.append_block(MessageBlock(Table(F)));C.append(A);A=Message(role=_A);G=MediaResources.get_local_test_document_uri(_G);A.append_block(MessageBlock(Document(G)));C.append(A);J=[Text];B=E.converter.convert(C,J);assert len(B)==len(C);assert all(isinstance(A[0].media,Text)for A in B);D=Message(role=_A);D.append_text(H);D.append_block(MessageBlock(Json({I:_B})));D.append_block(MessageBlock(Table(F)));D.append_block(MessageBlock(Document(G)));K=[Text,Json,Table,Document];B=E.converter.convert([D],K);assert len(B)==1;assert len(B[0].blocks)==4;assert isinstance(B[0][0].media,Text);assert isinstance(B[0][1].media,Json);assert isinstance(B[0][2].media,Table);assert isinstance(B[0][3].media,Document)
	def test_get_convertible_media_types(B):B.converter.add_default_converters();C={Text};A=B.converter.get_convertible_media_types(C);assert isinstance(A,set);assert Text in A;assert Json in A;C={Text,Json};A=B.converter.get_convertible_media_types(C);assert Text in A;assert Json in A;assert Table in A
	def test_get_convertable_media_types_empty_converters(A):A.converter.media_converters=[];B={Text};C=A.converter.get_convertible_media_types(B);assert C=={Text};B={Text,Json};C=A.converter.get_convertible_media_types(B);assert C=={Text,Json}
```

==========
Prog. Lang. Code File: test_media_converter_extra.py
Size: 5.24 kilobytes
==========

test_media_converter_extra.py
```py
_A='user'
from typing import Any,List,Tuple,Type
from litemind.agent.messages.message import Message
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.conversion.media_converter import MediaConverter
from litemind.media.media_base import MediaBase
from litemind.media.media_default import MediaDefault
from litemind.media.types.media_text import Text
class UnsupportedMedia(MediaDefault):
	def __init__(A,data):A.data=data
	def get_extension(A):return'unsupported'
	def get_content(A):return A.data
class IntermediateMedia(MediaDefault):
	def __init__(A,data):A.data=data
	def get_extension(A):return'intermediate'
	def get_content(A):return A.data
class FailingMedia(MediaDefault):
	def __init__(A):0
	def get_extension(A):return'failing'
	def get_content(A):return A.data
class IntermediateConverter(BaseConverter):
	def rule(A):return[(UnsupportedMedia,[IntermediateMedia])]
	def can_convert(A,media):return isinstance(media,UnsupportedMedia)
	def convert(A,media):return[IntermediateMedia(f"Converted from {media.data}")]
class IntermediateToTextConverter(BaseConverter):
	def rule(A):return[(IntermediateMedia,[Text])]
	def can_convert(A,media):return isinstance(media,IntermediateMedia)
	def convert(A,media):return[Text(f"Final conversion: {media.data}")]
class FailingConverter(BaseConverter):
	def rule(A):return[(Text,[Text])]
	def can_convert(A,media):return isinstance(media,FailingMedia)
	def convert(A,media):raise RuntimeError('Conversion failed!')
class TestMessageConverterExtra:
	def setup_method(A):A.converter=MediaConverter()
	def test_recursive_conversion(B):D=IntermediateConverter();E=IntermediateToTextConverter();B.converter.add_media_converter(D);B.converter.add_media_converter(E);C=Message(role=_A);C.append_block(MessageBlock(UnsupportedMedia('test_data')));A=B.converter.convert([C],[Text],recursive=False);assert len(A)==1;assert isinstance(A[0][0].media,IntermediateMedia);A=B.converter.convert([C],[Text],recursive=True);assert len(A)==1;assert isinstance(A[0][0].media,Text);assert'Final conversion: Converted from test_data'in A[0][0].media.text
	def test_unsupported_media_type(D):C='no_converter_available';B=Message(role=_A);B.append_block(MessageBlock(UnsupportedMedia(C)));A=D.converter.convert([B],[Text]);assert len(A)==1;assert isinstance(A[0][0].media,UnsupportedMedia);assert A[0][0].media.data==C
	def test_failing_converter(C):D=FailingConverter();C.converter.add_media_converter(D);B=Message(role=_A);B.append_block(MessageBlock(FailingMedia()));A=C.converter.convert([B],[Text]);assert len(A)==1;assert len(A[0].blocks)==2;assert isinstance(A[0][0].media,Text);assert isinstance(A[0][1].media,FailingMedia);assert B[0]==A[0][1]
```

==========
Prog. Lang. Code File: test_media_converter_per_type.py
Size: 16.46 kilobytes
==========

test_media_converter_per_type.py
```py
_A='user'
from pandas import DataFrame
from pydantic import BaseModel
from litemind.agent.messages.message import Message
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.conversion.media_converter import MediaConverter
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_code import Code
from litemind.media.types.media_document import Document
from litemind.media.types.media_file import File
from litemind.media.types.media_image import Image
from litemind.media.types.media_json import Json
from litemind.media.types.media_ndimage import NdImage
from litemind.media.types.media_object import Object
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
from litemind.ressources.media_resources import MediaResources
class TestMessageConverterPerType:
	def setup_method(A):A.converter=MediaConverter()
	def test_convert_audio_media(C):C.converter.add_default_converters();E=MediaResources.get_local_test_audio_uri('zebrahub_short.mp3');F=Audio(E);D=Message(role=_A);D.append_block(MessageBlock(F));A=C.converter.convert([D],[Text]);assert len(A)==1;assert isinstance(A[0][0].media,Text);assert len(A[0][0].media.text)>0;B=A[0][0].media.text.lower();assert isinstance(B,str);assert'embryo'in B;assert'development'in B;assert'zebrafish'in B
	def test_convert_code_media(C):
		C.converter.add_default_converters();E=MediaResources.get_local_test_document_uri('fib.cpp');F=E.replace('file://','')
		with open(F,'r')as G:H=G.read()
		I=Code(H,lang='cpp');D=Message(role=_A);D.append_block(MessageBlock(I));B=C.converter.convert([D],[Text]);assert len(B)==1;assert isinstance(B[0][0].media,Text);A=B[0][0].media.text.lower();assert isinstance(A,str);assert len(A)>0;assert'fibonacci'in A;assert'recursion'in A;assert'fib(n - 1)'in A;assert'#include <bits/stdc++.h>'in A;assert'using namespace std;'in A;assert'int main()'in A;assert'return 0;'in A
	def test_convert_docx_document_media(C):C.converter.add_default_converters();E=MediaResources.get_local_test_document_uri('maya_takahashi_cv.docx');F=Document(E);D=Message(role=_A);D.append_block(MessageBlock(F));B=C.converter.convert([D],[Text]);assert len(B)==1;assert isinstance(B[0][0].media,Text);A=str(B).lower();assert'skills'in A;assert'(206)-555-7890'in A;assert'maya takahashi'in A;assert'cv'in A;assert'lymphocyte'in A
	def test_convert_html_document_media(C):C.converter.add_default_converters();E=MediaResources.get_local_test_document_uri('sample.html');F=Document(E);D=Message(role=_A);D.append_block(MessageBlock(F));B=C.converter.convert([D],[Text]);assert len(B)==1;assert isinstance(B[0][0].media,Text);assert len(B[0][0].media.text)>0;A=str(B).lower();assert'---'in A;assert'heading'in A;assert'ipsum'in A;assert'#include'in A;assert'explanation'in A;assert'recording'in A
	def test_convert_pdf_document_media(C):C.converter.add_default_converters();E=MediaResources.get_local_test_document_uri('intracktive_preprint.pdf');F=Document(E);D=Message(role=_A);D.append_block(MessageBlock(F));B=C.converter.convert([D],[Text]);assert len(B)==1;assert isinstance(B[0][0].media,Text);A=str(B[0]);assert isinstance(A,str);assert len(A)>0;assert'cell'in A;assert'tracking'in A;assert'intracktive'in A
	def test_convert_file_media(C):C.converter.add_default_converters();E=MediaResources.get_local_test_other_uri('file.dat');F=File(E);D=Message(role=_A);D.append_block(MessageBlock(F));B=C.converter.convert([D],[Text]);assert len(B)==1;assert isinstance(B[0][0].media,Text);A=B[0][0].media.text;assert isinstance(A,str);assert len(A)>0;assert"Description of file: 'file.dat'"in A;assert'fb 00 00 00 00'in A;assert'File Size: 6032 bytes (5.89 KB)'in A
	def test_convert_image_media(C):C.converter.add_default_converters();A=MediaResources.get_local_test_image_uri('cat.jpg');E=Image(A);D=Message(role=_A);D.append_block(MessageBlock(E));B=C.converter.convert([D],[Text,Image]);assert len(B)==1;assert isinstance(B[0][0].media,Image);A=B[0][0].media.uri;assert isinstance(A,str);assert len(A)>0;assert'cat'in A
	def test_convert_json_media(C):J='value';I='key';H='test';G='nested';F='values';E='name';C.converter.add_default_converters();K={E:H,F:[1,2,3],G:{I:J}};L=Json(K);D=Message(role=_A);D.append_block(MessageBlock(L));B=C.converter.convert([D],[Text]);assert len(B)==1;assert isinstance(B[0][0].media,Text);A=B[0][0].media.text.lower();assert isinstance(A,str);assert len(A)>0;assert E in A;assert H in A;assert F in A;assert G in A;assert I in A;assert J in A
	def test_convert_ndimage_media(C):C.converter.add_default_converters();E=MediaResources.get_local_test_ndimage_uri('tubhiswt_C1.ome.tif');F=NdImage(E);D=Message(role=_A);D.append_block(MessageBlock(F));B=C.converter.convert([D],[Text]);assert len(B)==1;assert isinstance(B[0][0].media,Text);A=str(B);assert isinstance(A,str);assert len(A)>0;assert"## nD Image 'tubhiswt_C1.ome.tif'"in A;assert'**Spatial dimensions:** 1, 2, 3'in A;assert'**Dimensions:** 2 × 20 × 512 × 512'in A;assert'**Data type:** uint8'in A;assert'### Channel: x=1'in A;assert'### Maximum Intensity Projection: zt-plane'in A;assert'**Value range:** [0, 215]'in A
	def test_convert_object_media(C):
		E='TestObject';C.converter.add_default_converters()
		class F(BaseModel):
			name:str;value:int;description:str='This is a sample object for testing.'
			def __str__(A):return f"SampleObjectForTest(name='{A.name}', value={A.value}, description='{A.description}')"
			def __repr__(A):return A.__str__()
		G=F(name=E,value=123);H=Object(G);D=Message(role=_A);D.append_block(MessageBlock(H));B=C.converter.convert([D],[Text]);assert len(B)==1;assert isinstance(B[0][0].media,Text);A=str(B[0][0]);assert isinstance(A,str);assert len(A)>0;assert'```json'in A;assert'\n```'in A;assert E in A;assert'This is a sample object for testing'in A;assert':123'in A
	def test_convert_table_media(C):G='San Francisco';F='Alice';E='Name';C.converter.add_default_converters();H={E:[F,'Bob','Charlie'],'Age':[30,24,35],'City':['New York',G,'London']};I=Table.from_dataframe(DataFrame(H));D=Message(role=_A);D.append_block(MessageBlock(I));B=C.converter.convert([D],[Text]);assert len(B)==1;assert isinstance(B[0][0].media,Text);A=B[0][0].media.text;assert isinstance(A,str);assert len(A)>0;assert E in A;assert F in A;assert'24'in A;assert G in A
	def test_convert_text_media(C):C.converter.add_default_converters();D='This is a sample text for testing conversion.';F=Text(text=D);E=Message(role=_A);E.append_block(MessageBlock(F));A=C.converter.convert([E],[Text]);assert len(A)==1;assert isinstance(A[0][0].media,Text);B=A[0][0].media.text;assert isinstance(B,str);assert len(B)>0;assert B==D
	def test_convert_video_media(C):C.converter.add_default_converters();E=MediaResources.get_local_test_video_uri('job_interview.mp4');F=Video(E);D=Message(role=_A);D.append_block(MessageBlock(F));A=C.converter.convert([D],[Text,Image]);assert len(A)==1;assert len(A[0].blocks)>=100;assert isinstance(A[0][0].media,Text);assert all(isinstance(A.media,Text)or isinstance(A.media,Image)for A in A[0].blocks);B=A[0][-1].media.text.lower();assert'job'in B;assert'glimmer'in B;assert'florida'in B
```

==========
Prog. Lang. Code File: test_media_converter_rules.py
Size: 4.44 kilobytes
==========

test_media_converter_rules.py
```py
from typing import List,Tuple,Type
from litemind.media.conversion.converters.base_converter import BaseConverter
from litemind.media.conversion.media_converter import MediaConverter
from litemind.media.media_base import MediaBase
from litemind.media.types.media_audio import Audio
from litemind.media.types.media_image import Image
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
class MockConverter(BaseConverter):
	def __init__(A,rules):A.conversion_rules=rules
	def rule(A):return A.conversion_rules
	def can_convert(A,media):return True
	def convert(A,media):return[]
def test_can_convert_within_basic():A=MediaConverter();B=MockConverter([(Video,[Text,Image,Audio])]);A.add_media_converter(B);assert A.can_convert_within(Video,{Text,Image,Audio});assert not A.can_convert_within(Video,{Video,Text})
def test_can_convert_within_multi_step():A=MediaConverter();A.add_media_converter(MockConverter([(Video,[Audio,Image])]));A.add_media_converter(MockConverter([(Audio,[Text])]));assert A.can_convert_within(Video,{Video,Audio,Image,Text});assert not A.can_convert_within(Video,{Video,Audio,Image});assert not A.can_convert_within(Video,{Video,Image,Text})
def test_can_convert_within_cyclic_conversions():A=MediaConverter();A.add_media_converter(MockConverter([(Video,[Audio])]));A.add_media_converter(MockConverter([(Audio,[Image])]));A.add_media_converter(MockConverter([(Image,[Video])]));assert A.can_convert_within(Video,{Video,Audio,Image});assert not A.can_convert_within(Video,{Video,Audio});assert not A.can_convert_within(Video,{Video,Image});assert not A.can_convert_within(Video,{Audio,Image})
def test_can_convert_within_branching_paths():A=MediaConverter();A.add_media_converter(MockConverter([(Video,[Audio,Image])]));A.add_media_converter(MockConverter([(Audio,[Text])]));A.add_media_converter(MockConverter([(Image,[Table])]));assert A.can_convert_within(Video,{Video,Audio,Image,Text,Table});assert not A.can_convert_within(Video,{Video,Audio,Image,Text});assert not A.can_convert_within(Video,{Video,Audio,Image,Table})
def test_can_convert_within_empty_converter_list():A=MediaConverter();assert A.can_convert_within(Video,{Video});assert A.can_convert_within(Video,{Video,Text});assert not A.can_convert_within(Video,{Text})
def test_can_convert_within_disjoint_conversion_paths():A=MediaConverter();A.add_media_converter(MockConverter([(Video,[Audio]),(Image,[Table])]));assert A.can_convert_within(Video,{Video,Audio});assert A.can_convert_within(Image,{Image,Table})
def test_can_convert_within_with_default_converters():A=MediaConverter();A.add_default_converters();B=A.can_convert_within(Video,{Video,Text,Audio,Image});assert isinstance(B,bool)
```




==========
Prog. Lang. Code File: test_action.py
Size: 1.65 kilobytes
==========

test_action.py
```py
import pytest
from litemind.agent.messages.actions.tool_use import ToolUse
from litemind.media.types.media_action import Action
class TestAction:
	@pytest.fixture
	def sample_tool_use(self):return ToolUse(tool_name='calculator',arguments={'a':1,'b':2},result=3,id='test-id')
	def test_init_with_pydantic_model(C,sample_tool_use):A=sample_tool_use;B=Action(action=A);assert B.action==A
	def test_init_invalid_none(A):
		with pytest.raises(ValueError,match='Object cannot be None'):Action(action=None)
	def test_init_with_non_action(A):
		with pytest.raises(ValueError,match='Object must be an ActionBase'):Action(action='not a model')
	def test_str(C,sample_tool_use):B=Action(action=sample_tool_use);A=str(B);assert isinstance(A,str);assert'calculator(a=1, b=2)=3'in A
	def test_kwargs_are_stored(B,sample_tool_use):A=Action(action=sample_tool_use,custom_attr='test_value');assert'custom_attr'in A.attributes
```

==========
Prog. Lang. Code File: test_audio.py
Size: 4.67 kilobytes
==========

test_audio.py
```py
_B='.wav'
_A=False
import os,tempfile,numpy as np,pytest
from litemind.media.types.media_audio import Audio
@pytest.fixture
def sample_audio_data():A=44100;B=.1;C=np.linspace(0,B,int(A*B),endpoint=_A);D=np.sin(2*np.pi*440*C);return D,A
@pytest.fixture
def temp_audio_file(sample_audio_data):
	B,C=sample_audio_data
	with tempfile.NamedTemporaryFile(suffix=_B,delete=_A)as D:A=D.name
	import soundfile as E;E.write(A,B,C);yield A
	if os.path.exists(A):os.remove(A)
def test_audio_initialization():A='file:///path/to/audio.wav';B=Audio(uri=A);assert B.uri==A
def test_from_data_default_filepath(sample_audio_data):C,D=sample_audio_data;A=Audio.from_data(data=C,sample_rate=D);assert A.uri.startswith('file://');B=A.uri[7:];assert os.path.exists(B);os.remove(B)
def test_from_data_custom_filepath(sample_audio_data):
	B,C=sample_audio_data
	with tempfile.NamedTemporaryFile(suffix=_B,delete=_A)as D:A=D.name
	try:E=Audio.from_data(data=B,sample_rate=C,filepath=A);assert E.uri==f"file://{A}";import soundfile as F;G,H=F.read(A);assert H==C;assert np.allclose(G,B,atol=.001,rtol=.001)
	finally:
		if os.path.exists(A):os.remove(A)
def test_load_from_uri(temp_audio_file,sample_audio_data):B,C=sample_audio_data;A=Audio(uri=f"file://{temp_audio_file}");A.load_from_uri();assert hasattr(A,'data');assert A.samplerate==C;assert A.num_channels==1;assert A.dtype==B.dtype;assert np.allclose(A.data,B,atol=.001,rtol=.001)
def test_stereo_audio():
	D=44100;E=.1;F=np.linspace(0,E,int(D*E),endpoint=_A);G=np.sin(2*np.pi*440*F);H=np.sin(2*np.pi*880*F);C=np.column_stack((G,H))
	with tempfile.NamedTemporaryFile(suffix=_B,delete=_A)as I:A=I.name
	try:import soundfile as J;J.write(A,C,D);B=Audio(uri=f"file://{A}");B.load_from_uri();assert B.num_channels==2;assert B.data.shape==C.shape;assert np.allclose(B.data,C,atol=.001,rtol=.001)
	finally:
		if os.path.exists(A):os.remove(A)
def test_different_audio_formats():
	B=44100;D=.1;F=np.linspace(0,D,int(B*D),endpoint=_A);E=np.sin(2*np.pi*440*F)
	for(G,H)in[('WAV',_B),('FLAC','.flac')]:
		with tempfile.NamedTemporaryFile(suffix=H,delete=_A)as I:A=I.name
		try:import soundfile as J;J.write(A,E,B,format=G);C=Audio(uri=f"file://{A}");C.load_from_uri();assert C.samplerate==B;assert np.allclose(C.data,E,atol=.001,rtol=.001)
		finally:
			if os.path.exists(A):os.remove(A)
def test_from_data_non_array_input():
	with pytest.raises(Exception):Audio.from_data(data='not an array',sample_rate=44100)
```

==========
Prog. Lang. Code File: test_code.py
Size: 4.87 kilobytes
==========

test_code.py
```py
_D="```python\nprint('Hello, World!')\n```"
_C='javascript'
_B="print('Hello, World!')"
_A='python'
import pytest
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.types.media_code import Code
from litemind.media.types.media_text import Text
class TestCode:
	@pytest.fixture
	def sample_code(self):return Code(code=_B,lang=_A)
	def test_code_initialization(D):A="print('Hello, world!')";B=_A;C=Code(code=A,lang=B);assert C.code==A;assert C.lang==B
	def test_init_valid(C):B='const x = 5;';A=Code(code=B,lang=_C);assert A.code==B;assert A.lang==_C
	def test_init_invalid_code(A):
		with pytest.raises(ValueError,match="Parameter 'code' must be a string, not <class 'int'>"):Code(code=123,lang=_A)
	def test_init_invalid_lang(A):
		with pytest.raises(ValueError,match="Parameter 'lang' must be a string, not <class 'str'>"):Code(code="print('test')",lang=123)
	def test_to_markdown(B,sample_code):A=sample_code.to_markdown();assert A==_D
	def test_to_markdown_2(E):A="function greet() {\n  console.log('Hello');\n}";B=_C;C=Code(code=A,lang=B);D=f"```{B}\n{A}\n```";assert C.to_markdown()==D
	def test_to_markdown_text_media(B,sample_code):A=sample_code.to_markdown_text_media();assert isinstance(A,Text);assert A.text==_D
	def test_to_markdown_text_media_2(E):A="def greet():\n    print('Hello')";B=_A;D=Code(code=A,lang=B);C=D.to_markdown_text_media();assert isinstance(C,Text);assert C.text==f"```{B}\n{A}\n```"
	def test_str(A,sample_code):assert str(sample_code)==_B
	def test_len(A,sample_code):assert len(sample_code)==len(_B)
	def test_to_message_block(C,sample_code):A=sample_code;B=A.to_message_block();assert isinstance(B,MessageBlock);assert B.media==A
	def test_multiline_code(C):A="def hello():\n    print('Hello World')\n\nhello()";B=Code(code=A,lang=_A);assert B.code==A;assert B.to_markdown()==f"```python\n{A}\n```"
	def test_empty_code(B):A=Code(code='',lang=_A);assert A.code=='';assert A.to_markdown()=='```python\n\n```'
	def test_str_representation(C):A='SELECT * FROM users;';B=Code(code=A,lang='sql');assert str(B)==A
	def test_code_with_multiline_content(D):A='def factorial(n):\n    if n <= 1:\n        return 1\n    return n * factorial(n-1)';B=_A;C=Code(code=A,lang=B);assert C.code==A;assert C.to_markdown()==f"```{B}\n{A}\n```"
	def test_code_with_special_characters(E):B="# Comment with *asterisks* and _underscores_\nprint('Hello')";C=_A;D=Code(code=B,lang=C);A=D.to_markdown();assert A==f"```{C}\n{B}\n```";assert'*asterisks*'in A;assert'_underscores_'in A
```

==========
Prog. Lang. Code File: test_document.py
Size: 5.08 kilobytes
==========

test_document.py
```py
_C='example.pdf'
_B='Dummy PDF content'
_A='file://'
import os,tempfile,pytest
from litemind.media.media_uri import MediaURI
from litemind.media.types.media_document import Document
class TestDocument:
	def test_document_initialization_with_valid_local_file(E):
		A=os.path.join(tempfile.gettempdir(),_C)
		with open(A,'w')as D:D.write(_B)
		B=_A+A;C=Document(uri=B);assert C.uri==B;assert C.extension==None;os.remove(A)
	def test_document_initialization_with_valid_local_file_and_extension(F):
		D='pdf';A=os.path.join(tempfile.gettempdir(),'example2.pdf')
		with open(A,'w')as E:E.write(_B)
		B=_A+A;C=Document(uri=B,extension=D);assert C.uri==B;assert C.extension==D;os.remove(A)
	def test_document_initialization_with_valid_remote_url(C):A='http://example.com/example.pdf';B=Document(uri=A);assert B.uri==A;assert B.extension==None
	def test_document_initialization_with_invalid_local_file_extension(B):
		A='example.sdse'
		with pytest.raises(ValueError,match="Invalid URI or local file path: 'example.sdse'."):Document(uri=A)
	def test_document_initialization_with_no_extension_remote_url(C):A='http://example.com/example';B=Document(uri=A);assert B.uri==A;assert B.extension==None
	def test_document_initialization_with_upper_case_extension(B):
		A='EXAMPLE.PDF'
		with pytest.raises(ValueError,match="Invalid URI or local file path: 'EXAMPLE.PDF'."):Document(uri=A)
	def test_document_initialization_with_mixed_case_extension(B):
		A='ExAmPlE.pDf'
		with pytest.raises(ValueError,match="Invalid URI or local file path: 'ExAmPlE.pDf'."):Document(uri=A)
	def test_inheritance(E):
		A=_C
		with open(A,'w')as B:B.write(_B)
		C=_A+A;D=Document(uri=C);assert isinstance(D,MediaURI);os.remove(A)
	def test_extract_text_from_pages(H):
		A=os.path.join(tempfile.gettempdir(),'test_document.pdf');from pypdf import PdfWriter as D;B=D();B.add_blank_page(width=216,height=720)
		with open(A,'wb')as E:B.write(E)
		F=_A+A;G=Document(uri=F);C=G.extract_text_from_pages();assert isinstance(C,list);assert len(C)>0;os.remove(A)
	def test_take_image_of_each_page(I):
		B=os.path.join(tempfile.gettempdir(),'test_document_2.pdf');from pypdf import PdfWriter as E;D=E();D.add_blank_page(width=216,height=720)
		with open(B,'wb')as F:D.write(F)
		G=_A+B;H=Document(uri=G);C=H.take_image_of_each_page();assert isinstance(C,list);assert len(C)>0
		for A in C:
			if A.startswith(_A):A=A[7:]
			assert os.path.exists(A);os.remove(A)
		os.remove(B)
```

==========
Prog. Lang. Code File: test_file.py
Size: 7.29 kilobytes
==========

test_file.py
```py
_C='.bin'
_B='Description of file:'
_A=False
import binascii,os,tempfile
from litemind.media.types.media_file import File
from litemind.media.types.media_text import Text
class TestFile:
	def test_initialization(D):
		with tempfile.NamedTemporaryFile(suffix='.txt')as A:A.write(b'test content');A.flush();B=f"file://{A.name}";C=File(uri=B);assert C.uri==B
	def test_to_text_media_with_test_file(G):
		with tempfile.NamedTemporaryFile(suffix='.dat',delete=_A)as C:C.write(b'Test binary data for file media');A=C.name
		try:E=f"file://{A}";F=File(uri=E);D=F.to_markdown_text_media();assert isinstance(D,Text);B=D.text;assert _B in B;assert f"- Absolute Path: {os.path.abspath(A)}"in B;assert'- File Size:'in B;assert'- MIME Type:'in B
		finally:
			if os.path.exists(A):os.remove(A)
	def test_to_text_media_small_file(I):
		with tempfile.NamedTemporaryFile(suffix=_C,delete=_A)as C:D=bytes([A%256 for A in range(50)]);C.write(D);B=C.name
		try:F=f"file://{B}";G=File(uri=F);E=G.to_markdown_text_media(hex_dump_length=128);assert isinstance(E,Text);A=E.text;assert _B in A;assert f"- Absolute Path: {os.path.abspath(B)}"in A;assert'- File Size: 50 bytes'in A;assert'Entire file content (50 bytes):'in A;H=binascii.hexlify(D).decode('ascii');assert H[:10]in A.replace(' ','')
		finally:
			if os.path.exists(B):os.remove(B)
	def test_to_text_media_large_file(I):
		C=64
		with tempfile.NamedTemporaryFile(suffix=_C,delete=_A)as D:F=bytes([A%256 for A in range(300)]);D.write(F);B=D.name
		try:G=f"file://{B}";H=File(uri=G);E=H.to_markdown_text_media(hex_dump_length=C);assert isinstance(E,Text);A=E.text;assert _B in A;assert f"- Absolute Path: {os.path.abspath(B)}"in A;assert'- File Size: 300 bytes'in A;assert f"First {C} bytes (hex):"in A;assert f"Last {C} bytes (hex):"in A
		finally:
			if os.path.exists(B):os.remove(B)
	def test_to_text_media_empty_file(G):
		with tempfile.NamedTemporaryFile(suffix=_C,delete=_A)as D:A=D.name
		try:E=f"file://{A}";F=File(uri=E);C=F.to_markdown_text_media();assert isinstance(C,Text);B=C.text;assert _B in B;assert f"- Absolute Path: {os.path.abspath(A)}"in B;assert'- File Size: 0 bytes'in B;assert'File is empty.'in B
		finally:
			if os.path.exists(A):os.remove(A)
	def test_to_text_media_binary_content(I):
		with tempfile.NamedTemporaryFile(suffix=_C,delete=_A)as B:C=bytes([0,255,10,13,27,7,9]+[65+A%26 for A in range(20)]);B.write(C);A=B.name
		try:F=f"file://{A}";G=File(uri=F);H=G.to_markdown_text_media();D=H.text;assert f"- File Size: {len(C)} bytes"in D;E=D.replace(' ','').lower();assert'00'in E;assert'ff'in E
		finally:
			if os.path.exists(A):os.remove(A)
	def test_to_text_media_error_handling(E):
		A='Expected an error but none was raised.';B='/path/to/nonexistent/file.bin';C=f"file://{B}";D=File(uri=C)
		try:F=D.to_markdown_text_media();assert _A,A
		except ValueError:assert True,A
	def test_load_from_uri(F):
		with tempfile.NamedTemporaryFile(suffix='.txt')as A:B=b'test file content';A.write(B);A.flush();C=f"file://{A.name}";D=File(uri=C);E=D.load_from_uri();assert E==B
```

==========
Prog. Lang. Code File: test_image.py
Size: 1.00 kilobytes
==========

test_image.py
```py
import numpy as np
from litemind.media.types.media_image import Image
from litemind.ressources.media_resources import MediaResources
class TestImage:
	def test_init_with_path(C):A=MediaResources.get_local_test_image_uri('future.jpeg');B=Image(uri=A);assert B.uri==A
	def test_init_with_array(C):A=np.zeros((100,100,3),dtype=np.uint8);B=Image.from_data(array=A);assert np.array_equal(B.array,A);assert B.uri is not None
	def test_str(D):A='panda.jpg';B=MediaResources.get_local_test_image_uri(A);C=Image(uri=B);assert A in str(C)
```

==========
Prog. Lang. Code File: test_json.py
Size: 2.36 kilobytes
==========

test_json.py
```py
_D='Test'
_C='test'
_B='value'
_A='name'
import pytest
from litemind.media.types.media_json import Json
class TestJson:
	def test_init(C):A={_A:_C,_B:42};B=Json(A);assert B.get_content()==A
	def test_init_with_string(C):A='{"name": "test", "value": 42}';B=Json(A);assert B.get_content()=={_A:_C,_B:42}
	def test_str(C):B={_A:_C,_B:42};A=Json(B);assert _A in str(A)and _C in str(A)and _B in str(A)and'42'in str(A)
	def test_len(C):A={_A:_C,_B:42};B=Json(A);assert len(B)==29
	def test_invalid_json(A):
		with pytest.raises(ValueError):Json('not valid json')
	def test_init_with_valid_json_str(C):B='{"name": "Test", "value": 123}';A=Json(B);assert A.json[_A]==_D;assert A.json[_B]==123
	def test_init_with_invalid_json_str(C):
		A='{"name": "Test", "value": }'
		with pytest.raises(ValueError)as B:Json(A)
		assert'Invalid JSON string'in str(B.value)
	def test_init_with_valid_json_data(C):B={_A:_D,_B:123};A=Json(B);assert A.json[_A]==_D;assert A.json[_B]==123
	def test_str_representation(C):A='{"a": 1}';B=Json(A);assert str(B)==A;assert repr(B)==A
	def test_markdown_output(D):A=Json({'b':2});B=A.to_markdown_string();assert B.startswith('```json\n');assert B.endswith('\n```');C=A.to_markdown_text_media();assert'```json'in str(C)
```

==========
Prog. Lang. Code File: test_media_base.py
Size: 880 bytes
==========

test_media_base.py
```py
from typing import Any
import pytest
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.media_base import MediaBase
class TestMediaBase:
	def test_initialization(C):
		class A(MediaBase):
			def get_content(A):return'Concrete content'
			def __str__(A):return'ConcreteMedia'
			def __len__(A):return 0
			def to_message_block(A):return MessageBlock(media=A)
		B=A();assert isinstance(B,MediaBase)
	def test_abstract_methods(A):
		with pytest.raises(TypeError):MediaBase()
```

==========
Prog. Lang. Code File: test_media_pickle_serialisation.py
Size: 6.22 kilobytes
==========

test_media_pickle_serialisation.py
```py
_A='python'
import pandas as pd
from pydantic import BaseModel
from litemind.media.types.media_code import Code
from litemind.media.types.media_document import Document
from litemind.media.types.media_image import Image
from litemind.media.types.media_json import Json
from litemind.media.types.media_object import Object
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
from litemind.media.types.media_video import Video
def test_text_pickle_serialization():A=Text(text='Some text',extra_data=123);B=A.to_pickle();C=Text.from_pickle(B);assert C==A;D=A.to_base64();E=Text.from_base64(D);assert E==A
def test_image_pickle_serialization():A=Image(uri='file://path/to/image.png',attributes={'width':800,'height':600});B=A.to_pickle();C=Image.from_pickle(B);assert C==A;D=A.to_base64();E=Image.from_base64(D);assert E==A
def test_video_pickle_serialization():A=Video(uri='file://path/to/video.mp4',duration=120);B=A.to_pickle();C=Video.from_pickle(B);assert C==A;D=A.to_base64();E=Video.from_base64(D);assert E==A
def test_code_pickle_serialization():A=Code(code="print('Hello world')",lang=_A);B=A.to_pickle();C=Code.from_pickle(B);assert C==A;D=A.to_base64();E=Code.from_base64(D);assert E==A
def test_document_pickle_serialization():A=Document(uri='file://path/to/document.pdf',title='My Document');B=A.to_pickle();C=Document.from_pickle(B);assert C==A;D=A.to_base64();E=Document.from_base64(D);assert E==A
def test_json_pickle_serialization():A=Json(json={'key':'value','nested':[1,2,3]});B=A.to_pickle();C=Json.from_pickle(B);assert C==A;D=A.to_base64();E=Json.from_base64(D);assert E==A
class CustomObject(BaseModel):type:str;data:int
def test_object_pickle_serialization():A=Object(object_=CustomObject(type='example',data=42),attributes={'key':'value'});B=A.to_pickle();C=Object.from_pickle(B);assert C==A;D=A.to_base64();E=Object.from_base64(D);assert E==A
def test_table_pickle_serialization():B=pd.DataFrame({'Col1':['A','C'],'Col2':['B','D']});A=Table.from_dataframe(B);C=A.to_pickle();D=Table.from_pickle(C);assert D==A;E=A.to_base64();F=Table.from_base64(E);assert F==A
def test_complex_data_pickle_serialization():A=Json(json={'text':Text(text='Nested text'),'code':Code(code="print('Hello')",lang=_A),'numbers':[1,2,3,4,5],'nested_dict':{'key1':'value1','key2':42,'key3':[True,False,None]}});B=A.to_pickle();C=Json.from_pickle(B);assert C==A;D=A.to_base64();E=Json.from_base64(D);assert E==A
def test_large_data_pickle_serialization():B='x'*100000;A=Text(text=B);C=A.to_pickle();D=Text.from_pickle(C);assert D==A;E=A.to_base64();F=Text.from_base64(E);assert F==A
def test_circular_reference_pickle_serialization():C='ref_to_obj2';B='ref_to_obj1';A={};G={B:A};A[C]=G;D=Json(json=A);H=D.to_pickle();E=Json.from_pickle(H);assert E.json[C][B]is E.json;I=D.to_base64();F=Json.from_base64(I);assert F.json[C][B]is F.json
```

==========
Prog. Lang. Code File: test_media_uri.py
Size: 5.36 kilobytes
==========

test_media_uri.py
```py
_F='https://example.com/image'
_E='text_file'
_D='file:///path/to/file.txt'
_C='png'
_B='jpg'
_A='https://example.com/image.jpg'
import tempfile
from pathlib import Path
import pytest
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.media_uri import MediaURI
class MinimalMediaURI(MediaURI):
	def load_from_uri(A):0
class TestMediaURI:
	@pytest.fixture
	def setup_test_files(self):
		with tempfile.TemporaryDirectory()as A:
			C=Path(A)/'test.txt'
			with open(C,'w')as B:B.write('test content')
			D=Path(A)/'test.jpg'
			with open(D,'wb')as B:B.write(b'dummy image data')
			yield{_E:str(C),'image_file':str(D),'temp_dir':A}
	def test_init_with_uri(D):B=_A;A=MinimalMediaURI(uri=B);assert A.uri==B;assert A.extension is None;C=_D;A=MinimalMediaURI(uri=C);assert A.uri==C;A=MinimalMediaURI(uri=B,extension=_C);assert A.extension==_C
	def test_is_local(E):B=_A;A=MinimalMediaURI(uri=B);assert not A.is_local();C=_D;A=MinimalMediaURI(uri=C);assert A.is_local();D=_D;A=MinimalMediaURI(uri=D);assert A.is_local()
	def test_get_filename(C):
		B='image.jpg';A=MinimalMediaURI(uri=_A);assert A.get_filename()==B;A=MinimalMediaURI(uri='https://example.com/path/to/image.jpg');assert A.get_filename()==B;A=MinimalMediaURI(uri='file:///path/to/document.pdf');assert A.get_filename()=='document.pdf'
		try:A=MinimalMediaURI(uri='');assert False
		except ValueError:assert True
	def test_get_extension(B):A=MinimalMediaURI(uri=_A);assert A.get_extension()==_B;A=MinimalMediaURI(uri=_A,extension=_C);assert A.get_extension()==_C;A=MinimalMediaURI(uri=_F,extension=_B);assert A.get_extension()==_B
	def test_has_extension(B):A=MinimalMediaURI(uri=_A);assert A.has_extension(_B);assert not A.has_extension(_C);A=MinimalMediaURI(uri='https://example.com/image.JPG');assert A.has_extension(_B);A=MinimalMediaURI(uri=_F,extension=_B);assert A.has_extension(_B)
	def test_to_remote_or_data_uri(F,setup_test_files,monkeypatch):B=_A;A=MinimalMediaURI(uri=B);assert A.to_remote_or_data_uri()==B;D=setup_test_files[_E];E=f"file://{D}";A=MinimalMediaURI(uri=E);C=A.to_remote_or_data_uri();assert C.startswith('data:');assert';base64,'in C
	def test_to_message_block(C):A=MinimalMediaURI(uri=_A);B=A.to_message_block();assert isinstance(B,MessageBlock);assert B.media==A
	def test_string_representation(C):A=_A;B=MinimalMediaURI(uri=A);assert str(B)==A;assert repr(B)==f"MediaURI({A})";assert len(B)==len(A)
```

==========
Prog. Lang. Code File: test_ndimage.py
Size: 8.94 kilobytes
==========

test_ndimage.py
```py
_D='file:///dummy.tif'
_C='.npy'
_B=False
_A='mri.tif'
import os,tempfile,numpy as np,pytest
from litemind.media.types.media_image import Image
from litemind.media.types.media_ndimage import NdImage
from litemind.media.types.media_text import Text
from litemind.ressources.media_resources import MediaResources
class TestNdImage:
	def test_init(C):A=MediaResources.get_local_test_ndimage_uri(_A);B=NdImage(uri=A);assert B.uri==A;assert B.array is None
	def test_load_from_uri(C):B=MediaResources.get_local_test_ndimage_uri(_A);A=NdImage(uri=B);A.load_from_uri();assert A.array is not None;assert isinstance(A.array,np.ndarray);assert A.array.ndim>=2
	def test_str(C):A=MediaResources.get_local_test_ndimage_uri(_A);B=NdImage(uri=A);assert _A in str(B)
	def test_load_nonexistent_file(E):
		A=tempfile.mkdtemp();B=os.path.join(A,'nonexistent.tif');os.rmdir(A);C=NdImage(uri=f"file://{B}")
		with pytest.raises(Exception)as D:C.load_from_uri()
		assert'Local file not found:'in str(D.value)
	def test_to_text_and_2d_projections_mri(F):
		D=MediaResources.get_local_test_ndimage_uri(_A);C=NdImage(uri=D);C.load_from_uri();A=C.to_text_and_2d_projection_medias();assert len(A)>0;assert isinstance(A[0],Text);assert'nD Image'in A[0].text;E=' × '.join([str(A)for A in C.array.shape]);assert E in A[0].text;assert'Spatial dimensions'in A[0].text
		for B in range(1,len(A),2):
			assert isinstance(A[B],Text);assert'Maximum Intensity Projection'in A[B].text
			if B+1<len(A):assert isinstance(A[B+1],Image);assert A[B+1].uri is not None
	def test_to_text_and_2d_projections_tube(G):B=MediaResources.get_local_test_ndimage_uri('tubhiswt_C1.ome.tif');A=NdImage(uri=B);A.load_from_uri();C=A.to_text_and_2d_projection_medias();D=A.to_text_and_2d_projection_medias(channel_threshold=1000);E=C[0].text;F=D[0].text;assert E!=F
	def test_singleton_dimensions(F):
		D=np.zeros((50,1,100));A=tempfile.NamedTemporaryFile(suffix=_C,delete=_B);A.close();np.save(A.name,D)
		try:B=NdImage(uri=f"file://{A.name}");B.load_from_uri();C=B.to_text_and_2d_projection_medias();assert'Singleton dimensions'in C[0].text;E=sum(1 for A in C if isinstance(A,Image));assert E>0
		finally:os.unlink(A.name)
	def test_channel_dimensions(F):
		D=np.zeros((200,3,200));A=tempfile.NamedTemporaryFile(suffix=_C,delete=_B);A.close();np.save(A.name,D)
		try:B=NdImage(uri=f"file://{A.name}");B.load_from_uri();C=B.to_text_and_2d_projection_medias();assert'Channel-like dimensions'in C[0].text;E=[A for A in C if isinstance(A,Text)and'Channel:'in A.text];assert len(E)>0
		finally:os.unlink(A.name)
	def test_not_enough_dimensions(D):
		B=np.zeros(200);A=tempfile.NamedTemporaryFile(suffix=_C,delete=_B);A.close();np.save(A.name,B)
		try:C=NdImage(uri=f"file://{A.name}");C.load_from_uri();assert _B,'Expected an exception due to insufficient dimensions'
		except Exception as E:assert True,'Expected an exception above due to insufficient dimensions'
		finally:os.unlink(A.name)
	def test_normalize_for_display(D):C=NdImage(uri=_D);B=np.linspace(0,100,100).reshape(10,10);A=C._normalize_for_display(B);assert A.dtype==np.uint8;assert A.min()==0;assert A.max()==255;B=np.linspace(-50,50,100).reshape(10,10);A=C._normalize_for_display(B);assert A.min()==0;assert A.max()==255;B=np.linspace(0,100,100).reshape(10,10);B[0,0]=np.nan;A=C._normalize_for_display(B);assert not np.isnan(A).any();B=np.ones((10,10));A=C._normalize_for_display(B);assert np.all(A==0)
	def test_get_channel_indices(E):D=NdImage(uri=_D);B=[1];C=5,3,10;A=D._get_channel_indices(B,C);assert len(A)==3;assert A==[(0,),(1,),(2,)];B=[0,2];C=2,5,3;A=D._get_channel_indices(B,C);assert len(A)==6;assert(0,0)in A;assert(1,2)in A
```

==========
Prog. Lang. Code File: test_object.py
Size: 3.10 kilobytes
==========

test_object.py
```py
_D='```json'
_C='"value":1'
_B='"name":"Test"'
_A='Test'
import pytest
from pydantic import BaseModel
from litemind.media.types.media_json import Json
from litemind.media.types.media_object import Object
from litemind.media.types.media_text import Text
class ExampleModel(BaseModel):name:str;value:int
def test_object_initialization():A=ExampleModel(name=_A,value=1);B=Object(A);assert B.object==A
def test_object_initialization_with_kwargs():C='test';A=ExampleModel(name=_A,value=1);B=Object(A,some_kwarg=C);assert B.object==A;assert B.attributes=={'some_kwarg':C}
def test_object_initialization_none_object():
	with pytest.raises(ValueError,match='Object cannot be None'):Object(None)
def test_object_initialization_invalid_object():
	with pytest.raises(ValueError,match='Object must be a Pydantic BaseModel'):Object('not a BaseModel')
def test_to_json_string():B=ExampleModel(name=_A,value=1);C=Object(B);A=C.to_json_string();assert isinstance(A,str);assert _B in A;assert _C in A
def test_to_json_media():B=ExampleModel(name=_A,value=1);A=Object(B);C=A.to_json_media();assert isinstance(C,Json);assert'name'in A.to_json_string()and _A in A.to_json_string()and'value'in A.to_json_string()and'1'in A.to_json_string()
def test_to_markdown_string():B=ExampleModel(name=_A,value=1);C=Object(B);A=C.to_markdown_string();assert isinstance(A,str);assert _D in A;assert _B in A;assert _C in A
def test_to_markdown_text_media():B=ExampleModel(name=_A,value=1);C=Object(B);A=C.to_markdown_text_media();assert isinstance(A,Text);assert _D in A.text;assert _B in A.text;assert _C in A.text
def test_object_str_representation():B=ExampleModel(name=_A,value=1);A=Object(B);assert str(A)==A.to_json_string()
```

==========
Prog. Lang. Code File: test_table.py
Size: 4.92 kilobytes
==========

test_table.py
```py
_J=' 4 '
_I=' 1 '
_H='test.csv'
_G='col1,col2\n1,2\n3,4'
_F='|-------:|-------:|'
_E='|   col1 |   col2 |'
_D='```dataframe'
_C='col2'
_B='col1'
_A=None
import os,tempfile,pandas as pd,pytest
from litemind.media.types.media_table import Table
from litemind.media.types.media_text import Text
def test_table_initialization_with_uri():B='file://test.csv';A=Table(uri=B);assert A.uri==B;assert A.dataframe is _A
def test_table_initialization_with_dataframe():B=pd.DataFrame({_B:[1,2],_C:[3,4]});A=Table.from_dataframe(B);A.load_from_uri();assert A.dataframe.equals(B);assert A.uri is not _A
def test_table_initialization_no_uri_or_dataframe():
	with pytest.raises(TypeError):Table()
def test_from_dataframe():B=pd.DataFrame({_B:[1,2],_C:[3,4]});A=Table.from_dataframe(B);assert A.uri is not _A;assert A.dataframe is _A
def test_from_dataframe_with_filepath():
	C=pd.DataFrame({_B:[1,2],_C:[3,4]})
	with tempfile.NamedTemporaryFile(delete=False,suffix='.csv')as D:A=D.name;B=Table.from_dataframe(C,filepath=A);assert B.uri==f"file://{A}";assert B.dataframe is _A;os.remove(A)
def test_load_from_uri(tmp_path):C=_G;A=tmp_path/_H;A.write_text(C);D=f"file://{A}";B=Table(uri=D);B.load_from_uri();assert B.dataframe is not _A;E=pd.read_csv(A);pd.testing.assert_frame_equal(B.dataframe,E)
def test_load_from_uri_no_uri():
	A=Table.from_dataframe(pd.DataFrame({_B:[1,2],_C:[3,4]}));A.uri=_A
	with pytest.raises(ValueError,match='No URI provided to load the table data'):A.load_from_uri()
def test_to_markdown_with_uri(tmp_path):C=_G;B=tmp_path/_H;B.write_text(C);D=f"file://{B}";E=Table(uri=D);A=E.to_markdown();assert isinstance(A,str);assert _D in A;assert _E in A;assert _F in A;assert'|      1 |      2 |'in A;assert'|      3 |      4 |'in A
def test_to_markdown_with_dataframe():B=pd.DataFrame({_B:[1,2],_C:[3,4]});C=Table.from_dataframe(B);A=C.to_markdown();assert isinstance(A,str);assert _D in A;assert _E in A;assert _F in A;assert _I in A;assert _J in A
def test_to_markdown_text_media_with_dataframe():B=pd.DataFrame({_B:[1,2],_C:[3,4]});C=Table.from_dataframe(B);A=C.to_markdown_text_media();assert isinstance(A,Text);assert _D in A.text;assert _E in A.text;assert _F in A.text;assert _I in A.text;assert _J in A.text
def test_to_markdown_text_media_with_uri(tmp_path):C=_G;B=tmp_path/_H;B.write_text(C);D=f"file://{B}";E=Table(uri=D);A=E.to_markdown_text_media();assert isinstance(A,Text);assert _D in A.text;assert _E in A.text;assert _F in A.text;assert _I in A.text;assert _J in A.text
```

==========
Prog. Lang. Code File: test_text.py
Size: 727 bytes
==========

test_text.py
```py
_A='Hello, world!'
from litemind.media.types.media_text import Text
class TestText:
	def test_init(B):A=Text(text=_A);assert A.text==_A
	def test_str(B):A=Text(text=_A);assert str(A)==_A
	def test_len(B):A=Text(text=_A);assert len(A)==13
	def test_empty_text(B):A=Text(text='');assert A.text=='';assert len(A)==0
```

==========
Prog. Lang. Code File: test_video.py
Size: 3.78 kilobytes
==========

test_video.py
```py
_F='bunny.mp4'
_E='flying.mp4'
_D='resolution'
_C='duration'
_B='codec'
_A='bit_rate'
import numpy as np,pytest
from litemind.media.types.media_video import Video
from litemind.ressources.media_resources import MediaResources
from litemind.utils.ffmpeg_utils import is_ffmpeg_available
@pytest.fixture(scope='class',autouse=True)
def skip_if_ffmpeg_not_available(request):
	if not is_ffmpeg_available():pytest.skip('ffmpeg is not available, skipping all tests in this class.')
class TestVideo:
	def test_init_with_valid_uri(C):A=MediaResources.get_local_test_video_uri(_E);B=Video(uri=A);assert B.uri==A
	def test_init_with_invalid_uri(B):
		A=MediaResources.get_local_test_image_uri('future.jpeg')
		with pytest.raises(ValueError,match='Invalid video URI'):Video(uri=A)
	def test_init_with_valid_extension(C):
		if not is_ffmpeg_available():pytest.skip('ffmpeg is not available, skipping test.')
		A=MediaResources.get_local_test_video_uri(_F);B=Video(uri=A,extension='mp4');assert B.uri==A
	def test_init_with_invalid_extension(B):
		A=MediaResources.get_local_test_video_uri('lunar_park.mp4')
		with pytest.raises(ValueError,match='Invalid video extension'):Video(uri=A,extension='invalid')
	def test_load_from_uri(C):B=MediaResources.get_local_test_video_uri(_E);A=Video(uri=B);A.load_from_uri();assert hasattr(A,'array');assert isinstance(A.array,np.ndarray);assert A.array.ndim==4;assert hasattr(A,'info');assert isinstance(A.info,dict);assert _A in A.info and _B in A.info and _C in A.info and _D in A.info
	def test_get_metadata(D):B=MediaResources.get_local_test_video_uri(_F);A=Video(uri=B);C=A.get_video_info();assert isinstance(C,dict);assert _A in A.info and _B in A.info and _C in A.info and _D in A.info
	def test_video_properties(D):C=MediaResources.get_local_test_video_uri('lunar_park.mov');A=Video(uri=C);A.load_from_uri();assert A.array.shape[0]>0;assert A.array.shape[1]>0;assert A.array.shape[2]>0;assert A.array.shape[3]==3;B=A.get_video_info();assert _A in B and _B in B and _C in B and _D in B
```




==========
Prog. Lang. Code File: media_action.py
Size: 898 bytes
==========

media_action.py
```py
from litemind.agent.messages.actions.action_base import ActionBase
from litemind.media.media_default import MediaDefault
class Action(MediaDefault):
	def __init__(B,action,**C):
		A=action;super().__init__(**C)
		if A is None:raise ValueError(f"Object cannot be None")
		elif not isinstance(A,ActionBase):raise ValueError(f"Object must be an ActionBase, got {type(A)}")
		B.action=A
	def get_content(A):return A.action
```

==========
Prog. Lang. Code File: media_audio.py
Size: 4.01 kilobytes
==========

media_audio.py
```py
_A=None
import pathlib
from typing import Optional
import numpy
from litemind.media.media_uri import MediaURI
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class Audio(MediaURI):
	@classmethod
	def from_data(F,data,sample_rate,file_format=_A,filepath=_A):
		B=file_format;A=filepath
		if A is _A:import tempfile as C;A=C.NamedTemporaryFile(delete=False).name+'.wav'
		if B is _A:B='RAW'if A.lower().endswith('.raw')else'WAV'
		import soundfile as D;D.write(data=data,samplerate=sample_rate,file=A,format=B);E='file://'+A;return Audio(uri=E)
	def load_from_uri(A):B=uri_to_local_file_path(A.uri);import soundfile as C;A.data,A.samplerate=C.read(B);A.num_channels=A.data.shape[1]if len(A.data.shape)>1 else 1;A.dtype=A.data.dtype
	def get_raw_data(A):B=uri_to_local_file_path(A.uri);C=pathlib.Path(B).read_bytes();return C
	def get_info_markdown(A):
		try:
			if not hasattr(A,'data')or A.data is _A:A.load_from_uri()
			B=uri_to_local_file_path(A.uri);C=len(A.data)/A.samplerate;D=int(C//60);E=C%60;F=pathlib.Path(B).stat().st_size;G=F/1048576;H=f"""

- **Filename**: {pathlib.Path(B).name}
- **Duration**: {D}m {E:.2f}s
- **Sample Rate**: {A.samplerate} Hz
- **Channels**: {A.num_channels}
- **Data Type**: {A.dtype}
- **Samples**: {len(A.data)}
- **File Size**: {G:.2f} MB
""";return H
		except Exception as I:return f"## Error\nFailed to extract audio information: {str(I)}"
```

==========
Prog. Lang. Code File: media_code.py
Size: 1.30 kilobytes
==========

media_code.py
```py
from litemind.media.media_default import MediaDefault
from litemind.media.types.media_text import Text
class Code(MediaDefault):
	def __init__(B,code,lang):
		A=code
		if not isinstance(A,str):raise ValueError(f"Parameter 'code' must be a string, not {type(A)}")
		B.code=A
		if not isinstance(lang,str):raise ValueError(f"Parameter 'lang' must be a string, not {type(A)}")
		B.lang=lang.lower()
	def get_content(A):return A.code
	def to_markdown(A):B=f"```{A.lang}\n{A.code}\n```";return B
	def to_markdown_text_media(A):B=Text(A.to_markdown());return B
	def __str__(A):return A.code
```

==========
Prog. Lang. Code File: media_document.py
Size: 2.17 kilobytes
==========

media_document.py
```py
from typing import List,Optional
from litemind.media.media_uri import MediaURI
from litemind.utils.document_processing import extract_text_from_document_pages,take_images_of_each_document_page
from litemind.utils.file_types.file_types import is_document_file
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class Document(MediaURI):
	def __init__(B,uri,extension=None,**C):
		A=uri
		if A.startswith('http'):0
		elif A.startswith('local:'):
			D=A[6:]
			if not is_document_file(D):raise ValueError(f"Invalid local document URI: '{A}' (must have a valid document file extension)")
		super().__init__(uri=A,extension=extension,**C);B.markdown=None
	def load_from_uri(A):A.markdown=A.to_markdown()
	def extract_text_from_pages(A):B=uri_to_local_file_path(A.uri);C=extract_text_from_document_pages(B);return C
	def take_image_of_each_page(A):B=uri_to_local_file_path(A.uri);C=take_images_of_each_document_page(B);return C
	def to_markdown(B,media_converter=None):from litemind.agent.messages.message import Message as C;A=C(role='user');A.append_document(document_uri=B.uri);D=A.to_markdown(media_converter=media_converter);return D
```

==========
Prog. Lang. Code File: media_file.py
Size: 6.31 kilobytes
==========

media_file.py
```py
from typing import Optional
from litemind.media.media_uri import MediaURI
from litemind.media.types.media_text import Text
from litemind.utils.file_types.file_types import is_text_file,probe
class File(MediaURI):
	def __init__(B,uri,extension=None,**A):super().__init__(uri=uri,extension=extension,**A)
	def load_from_uri(A):
		B=A.to_local_file_path()
		with open(B,'rb')as C:A.data=C.read()
		return A.data
	def to_markdown_text_media(P,hex_dump_length=128):
		U='mime_type';T='Unknown';S='file_type';O=' ';N='ascii';M='\n';D=hex_dump_length;import binascii as H,os;C=P.to_local_file_path();I=probe(C)
		try:
			E=os.path.getsize(C);V=os.path.abspath(C);W=os.path.getmtime(C);from datetime import datetime as X;Y=X.fromtimestamp(W).strftime('%Y-%m-%d %H:%M:%S');A=[f"Description of file: '{P.get_filename()}':",f"- Absolute Path: {V}",f"- File Size: {E} bytes ({E/1024:.2f} KB)",f"- Last Modified: {Y}",f"- File Type: {I.get(S,T)}",f"- MIME Type: {I.get(U,T)}"]
			for(Q,R)in I.items():
				if Q not in[S,U]and R is not None:A.append(f"- {Q}: {R}")
			if E==0:A.append('\nFile is empty.');return Text(text=M.join(A))
			elif is_text_file(C):
				with open(C,'r',encoding='utf-8')as F:Z=F.read();A.append('\nFile content:');A.append(Z);return Text(text=M.join(A))
			else:
				with open(C,'rb')as F:
					if E<=D*2:
						a=F.read();J=H.hexlify(a).decode(N);A.append(f"\nEntire file content ({E} bytes):")
						for B in range(0,len(J),32):G=O.join(J[B:B+32][A:A+2]for A in range(0,min(32,len(J[B:B+32])),2));A.append(G)
					else:
						b=F.read(D);F.seek(max(0,E-D));c=F.read(D);K=H.hexlify(b).decode(N);L=H.hexlify(c).decode(N);A.append(f"\nFirst {D} bytes (hex):")
						for B in range(0,len(K),32):G=O.join(K[B:B+32][A:A+2]for A in range(0,min(32,len(K[B:B+32])),2));A.append(G)
						A.append(f"\nLast {D} bytes (hex):")
						for B in range(0,len(L),32):G=O.join(L[B:B+32][A:A+2]for A in range(0,min(32,len(L[B:B+32])),2));A.append(G)
		except Exception as d:A=[f"Error analyzing file: {str(d)}"]
		e=M.join(A);return Text(text=e)
```

==========
Prog. Lang. Code File: media_image.py
Size: 4.54 kilobytes
==========

media_image.py
```py
_A=None
from typing import Optional
import numpy
from litemind.media.media_uri import MediaURI
from litemind.utils.convert_image_to_jpg import convert_image_to_jpeg
from litemind.utils.convert_image_to_png import convert_image_to_png
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class Image(MediaURI):
	def __init__(A,uri,extension=_A,**B):super().__init__(uri=uri,extension=extension,**B);A.array=_A
	@classmethod
	def from_data(E,array,filepath=_A,format=_A):A=array;from PIL import Image as C;D=C.fromarray(A);B=Image.from_PIL_image(image=D,filepath=filepath,format=format);B.array=A;return B
	@classmethod
	def from_PIL_image(E,image,filepath=_A,format=_A):
		B=image;A=filepath
		if A is _A:import tempfile as C;A=C.NamedTemporaryFile(delete=False).name+'.png'
		B.save(A,format);D='file://'+A;B=Image(uri=D);return B
	def load_from_uri(A):
		C=uri_to_local_file_path(A.uri);from PIL import Image as D
		try:B=D.open(C);A.array=numpy.array(B);B.close()
		except FileNotFoundError:raise FileNotFoundError(f"Image file not found at URI: {A.uri}")
		except Exception as E:raise Exception(f"Could not open or decode image file: {A.uri}. Error: {E}")
	def open_pil_image(B,normalise_to_png=False):
		from PIL import Image as C;A=uri_to_local_file_path(B.uri)
		if normalise_to_png:A=convert_image_to_png(A)
		return C.open(A)
	def normalise_to_png(A):A.to_local_file_path();A.local_path=convert_image_to_png(A.local_path);return A.local_path
	def normalise_to_jpeg(A):A.to_local_file_path();A.local_path=convert_image_to_jpeg(A.local_path);return A.local_path
```

==========
Prog. Lang. Code File: media_json.py
Size: 1.91 kilobytes
==========

media_json.py
```py
from typing import Optional,Union
from litemind.media.media_default import MediaDefault
from litemind.media.types.media_text import Text
class Json(MediaDefault):
	def __init__(B,json,**C):
		A=json;super().__init__(**C)
		if not isinstance(A,(str,dict)):raise ValueError(f"json must be a string or a dictionary, not {type(A)}")
		if isinstance(A,str):
			try:import json as D;B.json=D.loads(A)
			except ValueError as E:raise ValueError(f"Invalid JSON string: {E}")
		else:B.json=A
	@classmethod
	def from_string(A,param):return A(json=param)
	def get_content(A):return A.json
	def to_markdown_string(A):B=f"```json\n{str(A)}\n```";return B
	def to_markdown_text_media(A):return Text(A.to_markdown_string())
	def __str__(A):import json as B;C=B.dumps(A.json);return C
	def __repr__(A):return str(A)
	def __len__(A):return len(str(A.json))
```

==========
Prog. Lang. Code File: media_ndimage.py
Size: 14.24 kilobytes
==========

media_ndimage.py
```py
_A=None
from typing import List,Optional,Union
import numpy
from arbol import aprint
from litemind.media.media_uri import MediaURI
from litemind.media.types.media_image import Image
from litemind.media.types.media_text import Text
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class NdImage(MediaURI):
	def __init__(A,uri,extension=_A,**B):super().__init__(uri=uri,extension=extension,**B);A.array=_A
	def load_from_uri(A):
		B=uri_to_local_file_path(A.uri)
		try:
			if B.lower().endswith('.npy'):import numpy as C;A.array=C.load(B)
			elif B.lower().endswith('.npz'):
				import numpy as C
				with C.load(B)as D:
					E=list(D.keys())
					if not E:raise Exception('NPZ file contains no arrays')
					A.array=D[E[0]]
			elif B.lower().endswith(('.tif','.tiff')):import tifffile as F;A.array=F.imread(B)
			else:import imageio.v3 as G;A.array=G.imread(B)
			aprint(f"Loaded array with shape: {A.array.shape}, dtype: {A.array.dtype}")
			if A.array.ndim<2:raise Exception(f"Image has only {A.array.ndim} dimensions, expected at least 2.")
		except Exception as H:raise Exception(f"Could not open or decode nD image file: {A.uri}. Error: {H}")
	def to_text_and_2d_projection_medias(A,channel_threshold=10):
		N=channel_threshold;I=', ';import numpy as F;B=[]
		if A.array is _A:A.load_from_uri()
		G=A.array.shape;R=' × '.join([str(A)for A in G]);O=A.array.ndim;S=str(A.array.dtype);T=F.nanmin(A.array);U=F.nanmax(A.array);V=A.get_filename();H=f"""## nD Image '{V}'
    **Dimensions:** {R}
    **Number of dimensions:** {O}
    **Data type:** {S}
    **Value range:** [{T:.3g}, {U:.3g}]
    """
		if A.array.size<1000000:W=F.nanmean(A.array);X=F.nanmedian(A.array);Y=F.nanstd(A.array);H+=f"**Mean:** {W:.3g}\n    **Median:** {X:.3g}\n    **Standard deviation:** {Y:.3g}\n    "
		J=[A for(A,B)in enumerate(G)if B==1];C=[A for(A,B)in enumerate(G)if 1<B<=N];D=[A for(A,B)in enumerate(G)if B>N and A not in J]
		if J:H+=f"\n**Singleton dimensions (ignored):** {I.join([str(A)for A in J])}\n"
		if C:H+=f"\n**Channel-like dimensions:** {I.join([str(A)for A in C])}\n"
		if D:H+=f"\n**Spatial dimensions:** {I.join([str(A)for A in D])}\n"
		B.append(Text(text=H))
		if len(D)<2:B.append(Text(text='**Note:** Not enough spatial dimensions to create 2D projections.'));return B
		E=['x','y','z','t','c','s']
		while len(E)<O:E.append(f"dim{len(E)}")
		K=[]
		for(Z,a)in enumerate(D):
			for b in range(Z+1,len(D)):c=D[b];K.append((a,c))
		if C:
			d=A._get_channel_indices(C,G)
			for P in d:
				Q=[]
				for(e,f)in zip(C,P):Q.append(f"{E[e]}={f}")
				g=f"### Channel: {I.join(Q)}\n";B.append(Text(text=g))
				for(L,M)in K:A._add_projection_to_result(L,M,E,B,channel_dims=C,channel_indices=P)
		else:
			for(L,M)in K:A._add_projection_to_result(L,M,E,B)
		return B
	def _get_channel_indices(C,channel_dims,dimensions):import itertools as A;B=[range(dimensions[A])for A in channel_dims];return list(A.product(*B))
	def _add_projection_to_result(H,axis1,axis2,dim_labels,result,channel_dims=_A,channel_indices=_A):D=result;C=dim_labels;B=axis2;A=axis1;import tempfile as I;from litemind.media.types.media_image import Image;from litemind.media.types.media_text import Text;E=C[A];F=C[B];J=f"### Maximum Intensity Projection: {E}{F}-plane\n\n    This image shows the maximum intensity projection along the {E}{F}-plane\n    (dimensions {A} and {B} of the original array).\n    ";D.append(Text(J));K=H._create_max_projection(A,B,channel_dims=channel_dims,channel_indices=channel_indices);G=I.NamedTemporaryFile(delete=False,suffix='.png');G.close();L=Image.from_data(K,filepath=G.name,format='PNG');D.append(L)
	def _create_max_projection(D,axis1,axis2,channel_dims=_A,channel_indices=_A):
		K=channel_indices;J=channel_dims;F=axis2;E=axis1;import numpy as B
		if D.array is _A:D.load_from_uri()
		if J is not _A and K is not _A:
			L=[slice(_A)]*D.array.ndim
			for(O,P)in zip(J,K):L[O]=P
			C=D.array[tuple(L)]
		else:C=D.array
		G=[A for(A,B)in enumerate(C.shape)if B==1]
		if G:
			C=B.squeeze(C,axis=tuple(G))
			for M in sorted(G):
				if E>M:E-=1
				if F>M:F-=1
		H=C.ndim
		if E>=H or F>=H:Q=B.zeros((100,100),dtype=B.uint8);return B.stack([Q]*3,axis=-1)
		N=tuple([A for A in range(H)if A!=E and A!=F])
		if N:A=B.max(C,axis=N)
		else:A=C
		if A.ndim>2:A=A[(0,)*(A.ndim-2)]
		A=D._normalize_for_display(A)
		if A.ndim==2:I=B.stack([A]*3,axis=-1)
		elif A.ndim==3 and A.shape[2]==3:I=A
		else:I=B.stack([A[:,:,0]]*3,axis=-1)
		return I.astype(B.uint8)
	def _normalize_for_display(H,array):
		import numpy as B;A=array.copy()
		if B.all(A==A.flat[0]):return B.zeros_like(A,dtype=B.uint8)
		D=A[B.isfinite(A)]
		if len(D)==0:return B.zeros_like(A,dtype=B.uint8)
		C,E=B.nanmin(D),B.nanmax(D);F,G=.5,99.5
		if A.size>100:C,E=B.nanpercentile(D,[F,G])
		A=B.nan_to_num(A,nan=C);A=B.clip(A,C,E)
		if E>C:A=((A-C)/(E-C)*255).astype(B.uint8)
		else:A=B.zeros_like(A,dtype=B.uint8)
		return A
```

==========
Prog. Lang. Code File: media_object.py
Size: 2.30 kilobytes
==========

media_object.py
```py
from typing import Any
from pydantic import BaseModel
from litemind.media.media_default import MediaDefault
from litemind.media.types.media_json import Json
from litemind.media.types.media_text import Text
class Object(MediaDefault):
	def __init__(B,object_,**C):
		A=object_;super().__init__(**C)
		if A is None:raise ValueError(f"Object cannot be None")
		elif not isinstance(A,BaseModel):raise ValueError(f"Object must be a Pydantic BaseModel, got {type(A)}")
		B.object=A
	def get_content(A):return A.object
	def to_json_string(A):return A.object.model_dump_json()
	def to_json_media(A):return Json(A.to_json_string())
	def to_markdown_string(A):B=A.to_json_string();C=f"```json\n{B}\n```";return C
	def to_markdown_text_media(A):return Text(A.to_markdown_string())
	def __str__(A):return A.to_json_string()
	def __len__(A):return len(A.to_json_string())
```

==========
Prog. Lang. Code File: media_table.py
Size: 3.07 kilobytes
==========

media_table.py
```py
_A=None
from typing import Optional
from pandas import DataFrame
from litemind.media.media_uri import MediaURI
from litemind.media.types.media_text import Text
from litemind.utils.load_table import load_table_from_uri
class Table(MediaURI):
	def __init__(C,uri,extension=_A,**D):
		B=extension;A=uri
		if B is not _A:
			if not A.endswith(B):raise ValueError(f"Invalid table URI: '{A}' (must have a valid table file extension)")
		super().__init__(uri=A,extension=B,**D);C.dataframe=_A
	@classmethod
	def from_dataframe(E,dataframe,filepath=_A):
		B=False;A=filepath
		if A is _A:import tempfile as C;A=C.NamedTemporaryFile(delete=B).name
		dataframe.to_csv(A,index=B);D='file://'+A;return Table(uri=D)
	@classmethod
	def from_csv_string(B,csv_table_str):
		A=csv_table_str;from io import StringIO as C;import pandas as D
		if not isinstance(A,str):raise ValueError(f"Parameter `csv_table_str` must be a string, not {type(A)}")
		E=D.read_csv(C(A));return B.from_dataframe(E)
	def load_from_uri(A):
		if A.uri is _A:raise ValueError('No URI provided to load the table data')
		A.dataframe=load_table_from_uri(A.uri)
	def to_dataframe(A):
		if A.dataframe is _A:A.load_from_uri()
		return A.dataframe
	def to_markdown(A):
		if A.dataframe is _A:A.load_from_uri()
		B=f"```dataframe\n{A.dataframe.to_markdown()}\n```";return B
	def to_markdown_text_media(A):B=A.to_markdown();return Text(B)
```

==========
Prog. Lang. Code File: media_text.py
Size: 769 bytes
==========

media_text.py
```py
from litemind.media.media_default import MediaDefault
class Text(MediaDefault):
	def __init__(B,text,**C):
		A=text;super().__init__(**C)
		if not isinstance(A,str):raise ValueError(f"Text must be a string, not {type(A)}")
		B.text=A
	def get_content(A):return A.text
	def __str__(A):return A.text
```

==========
Prog. Lang. Code File: media_types.py
Size: 1.12 kilobytes
==========

media_types.py
```py
from typing import List,Type
def all_media_types():from litemind.media.types.media_action import Action as A;from litemind.media.types.media_audio import Audio;from litemind.media.types.media_code import Code;from litemind.media.types.media_document import Document as B;from litemind.media.types.media_file import File;from litemind.media.types.media_image import Image;from litemind.media.types.media_json import Json;from litemind.media.types.media_ndimage import NdImage as C;from litemind.media.types.media_object import Object as D;from litemind.media.types.media_table import Table;from litemind.media.types.media_text import Text;from litemind.media.types.media_video import Video;E=[A,Audio,Code,B,File,Image,Json,C,D,Table,Text,Video];return E
```

==========
Prog. Lang. Code File: media_video.py
Size: 2.94 kilobytes
==========

media_video.py
```py
_A=None
from typing import List,Optional
from litemind.agent.messages.message_block import MessageBlock
from litemind.media.media_uri import MediaURI
from litemind.utils.ffmpeg_utils import convert_video_to_frames_and_audio,get_video_info,load_video_as_array
from litemind.utils.file_types.file_extensions import VIDEO_EXTS
from litemind.utils.file_types.file_types import is_video_file
from litemind.utils.normalise_uri_to_local_file_path import uri_to_local_file_path
class Video(MediaURI):
	def __init__(C,uri,extension=_A,**D):
		B=uri;A=extension
		if not is_video_file(B):raise ValueError(f"Invalid video URI: '{B}' (must have a valid video file extension)")
		if A is not _A:
			if'.'+A not in VIDEO_EXTS:raise ValueError(f"Invalid video extension: {A}")
		super().__init__(uri=B,extension=A,**D);C.array=_A;C.info=_A
	def load_from_uri(A):
		if A.array is not _A:return
		B=uri_to_local_file_path(A.uri);C=load_video_as_array(B);A.array=C;A.get_video_info()
	def get_video_info(A):
		if A.info is _A:B=get_video_info(A.uri);A.info=B
		return A.info
	def convert_to_frames_and_audio(A):B=A.to_local_file_path();C=convert_video_to_frames_and_audio(B);return C
```




==========
Prog. Lang. Code File: media_resources.py
Size: 2.22 kilobytes
==========

media_resources.py
```py
class MediaResources:
	@staticmethod
	def get_local_test_folder_path(folder_name):import os;A=os.path.dirname(__file__);B=os.path.join(A,folder_name);return B
	@staticmethod
	def get_local_test_file_uri(filetype,image_name):import os;A=os.path.dirname(__file__);B=os.path.join(A,os.path.join(f"{filetype}/",image_name));C='file://'+B;return C
	@staticmethod
	def get_local_test_image_uri(image_name):return MediaResources.get_local_test_file_uri('images',image_name)
	@staticmethod
	def get_local_test_ndimage_uri(image_name):return MediaResources.get_local_test_file_uri('ndimages',image_name)
	@staticmethod
	def get_local_test_audio_uri(image_name):return MediaResources.get_local_test_file_uri('audio',image_name)
	@staticmethod
	def get_local_test_video_uri(image_name):return MediaResources.get_local_test_file_uri('videos',image_name)
	@staticmethod
	def get_local_test_document_uri(doc_name):return MediaResources.get_local_test_file_uri('documents',doc_name)
	@staticmethod
	def get_local_test_table_uri(doc_name):return MediaResources.get_local_test_file_uri('tables',doc_name)
	@staticmethod
	def get_local_test_archive_uri(doc_name):return MediaResources.get_local_test_file_uri('archives',doc_name)
	@staticmethod
	def get_local_test_other_uri(doc_name):return MediaResources.get_local_test_file_uri('others',doc_name)
```













==========
Prog. Lang. Code File: test_media_ressources.py
Size: 1.70 kilobytes
==========

test_media_ressources.py
```py
_C='cat.jpg'
_B='images'
_A='file://'
import os
from litemind.ressources.media_resources import MediaResources
def test_get_local_test_folder_path_images():A=MediaResources.get_local_test_folder_path(_B);assert os.path.isdir(A)
def test_get_local_test_file_uri_images():A=MediaResources.get_local_test_file_uri(_B,_C);assert A.startswith(_A);assert os.path.exists(A.replace(_A,''))
def test_get_local_test_image_uri():A=MediaResources.get_local_test_image_uri(_C);assert A.startswith(_A);assert os.path.exists(A.replace(_A,''))
def test_get_local_test_audio_uri():A=MediaResources.get_local_test_audio_uri('harvard.wav');assert A.startswith(_A);assert os.path.exists(A.replace(_A,''))
def test_get_local_test_video_uri():A=MediaResources.get_local_test_video_uri('bunny.mp4');assert A.startswith(_A);assert os.path.exists(A.replace(_A,''))
def test_get_local_test_document_uri():A=MediaResources.get_local_test_document_uri('timaeus.txt');assert A.startswith(_A);assert os.path.exists(A.replace(_A,''))
def test_get_local_test_table_uri():A=MediaResources.get_local_test_table_uri('spreadsheet.csv');assert A.startswith(_A);assert os.path.exists(A.replace(_A,''))
def test_get_local_test_archive_uri():A=MediaResources.get_local_test_archive_uri('alexander.zip');assert A.startswith(_A);assert os.path.exists(A.replace(_A,''))
```







==========
Prog. Lang. Code File: litemind_tools.py
Size: 5.83 kilobytes
==========

litemind_tools.py
```py
import argparse,os
from typing import List,Type
from litemind import API_IMPLEMENTATIONS,DefaultApi
from litemind.apis.base_api import BaseApi
from litemind.apis.combined_api import CombinedApi
from litemind.apis.providers.anthropic.anthropic_api import AnthropicApi
from litemind.apis.providers.google.google_api import GeminiApi
from litemind.apis.providers.ollama.ollama_api import OllamaApi
from litemind.apis.providers.openai.openai_api import OpenAIApi
from litemind.tools.commands.codegen import codegen
from litemind.tools.commands.export_repo import export_repo
from litemind.tools.commands.scan import scan
def main():
	U='scan';T='export';S='combined';R='api';Q='codegen';O='all';N='*';J='ollama';I='claude';H='openai';G='gemini';C=None;P=argparse.ArgumentParser(description='Litemind command-line tool.');K=P.add_subparsers(dest='command');L=K.add_parser(Q,help='Generate files (such as a README.md) for a Python repository.');L.add_argument(R,choices=[G,H,I,J,S],default=S,nargs='?',help="The api to use for generating the files. Default is 'combined'.");L.add_argument('-m','--model',help="The specific model name to use. If not provided, the API's default model will be used.",default=C);L.add_argument('-f','--file',help='The specific file name to generate (no extension). If not provided, all files will be generated.',default=C);F=K.add_parser(T,help='Export the entire repository to a single file.');F.add_argument('-f','--folder-path',help='The path to the folder containing the repository to export.',default='.');F.add_argument('-o','--output-file',default='exported.txt',help='The path to the file to save the entire repository to.');F.add_argument('-e','--extensions',default=C,nargs=N,help='The list of allowed extensions for files to include in the export.');F.add_argument('-x','--exclude',default=C,nargs=N,help='The list of files to exclude from the export.');M=K.add_parser(U,help='Scan models from an API for supported features and generate a report.');M.add_argument(R,choices=[G,H,I,J,O],default=O,nargs='+',help="The API(s) to scan models from. Can specify multiple APIs (gemini, openai, claude, ollama, all). Default is 'all'.");M.add_argument('-m','--models',nargs=N,help='Specific model names to scan. If not provided, all available models will be scanned.',default=C);M.add_argument('-o','--output-dir',help='Directory to save scan results. If not provided, uses the default directory.',default=C);A=P.parse_args()
	if A.command==Q:
		if A.api==G:D=GeminiApi()
		elif A.api==H:D=OpenAIApi()
		elif A.api==I:D=AnthropicApi()
		elif A.api==J:D=OllamaApi()
		else:D=CombinedApi()
		V=os.getcwd();W=A.model if A.model else C;codegen(V,api=D,model_name=W,file_selection=A.file)
	elif A.command==T:export_repo(folder_path=A.folder_path,output_file=A.output_file,allowed_extensions=A.extensions,excluded_files=A.exclude)
	elif A.command==U:
		B=[]
		if O in A.api:
			B=list(API_IMPLEMENTATIONS)
			if CombinedApi in B:B.remove(CombinedApi)
			if DefaultApi in B:B.remove(DefaultApi)
		else:
			for E in A.api:
				if E==G:B.append(GeminiApi)
				elif E==H:B.append(OpenAIApi)
				elif E==I:B.append(AnthropicApi)
				elif E==J:B.append(OllamaApi)
				else:print(f"Unrecognized API: {E}. Skipping.")
		scan(apis=B,model_names=A.models,output_dir=A.output_dir)
```




==========
Prog. Lang. Code File: codegen.py
Size: 10.07 kilobytes
==========

codegen.py
```py
_B=False
_A=None
import os
from typing import Optional
from arbol import aprint,asection
from litemind import CombinedApi
from litemind.agent.agent import Agent
from litemind.agent.messages.message import Message
from litemind.apis.base_api import BaseApi
from litemind.apis.model_features import ModelFeatures
from litemind.media.conversion.converters.document_converter_python_minify import DocumentConverterPythonMinify
from litemind.tools.commands.utils import default_folder_scanning_parameters,parse_yaml
def codegen(folder_path,api=_A,model_name=_A,file_selection=_A):
	F=model_name;E='folder';D=folder_path;A=file_selection
	with asection(f"Generating files in folder: {D} using model {F} fromAPI: {api} and selection: {A}"):
		G=os.path.join(D,'.codegen')
		if A is not _A:A=A.strip()
		for B in os.listdir(G):
			if B.endswith('codegen.yml'):
				if A is not _A:
					if A not in B:continue
				B=os.path.join(G,B);C=parse_yaml(B)
				with asection(f"Parsed data from {B}:"):aprint(C)
				H=C[E].get('extensions',_A);aprint(H);I=C[E].get('excluded',_A);aprint(I);generate(prompt=C['prompt'],input_folder=os.path.join(D,C[E]['path']),output_file=os.path.join(D,C['file']),allowed_extensions=H,excluded_files=I,api=api,model_name=F)
def generate(prompt,input_folder,output_file,allowed_extensions=_A,excluded_files=_A,api=_A,model_name=_A,thinking=_B,minify_python=True,output_prompt_to_file=True):
	G=model_name;F=api;E=excluded_files;D=allowed_extensions;C=output_file;I=os.path.basename(C)
	with asection(f"Generating file: {I}"):
		if os.path.exists(C):aprint(f"File {C} already exists. Deleting it.");os.remove(C)
		D,E=default_folder_scanning_parameters(D,E);aprint(f"Allowed extensions ({len(D)}): {", ".join(D)}");aprint(f"Excluded files ({len(E)}): {", ".join(E)}")
		if F is _A:F=CombinedApi()
		if G is _A:
			G=F.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Document],non_features=[ModelFeatures.Thinking]if not thinking else _A)
			if G is _A:G=F.get_best_model(features=[ModelFeatures.TextGeneration,ModelFeatures.Document])
		K=Agent(api=F,model_name=G);A=Message(role='user');A.append_text(prompt);A.append_folder(input_folder,allowed_extensions=D,excluded_files=E,date_and_times=_B,file_sizes=_B);A.append_text(f"""

Please generate a detailed, complete and informative {I} file without any preamble or postamble:
""");from litemind.media.conversion.media_converter import MediaConverter as L;H=L()
		if minify_python:H.add_media_converter(DocumentConverterPythonMinify())
		H.add_default_converters();A=A.convert_media(media_converter=H);from litemind.utils.text_compressor import TextCompressor as M;N=M(schemes=['newlines','comments','repeats','trailing'],max_repeats=10);A=A.compress_text(text_compressor=N)
		with asection(f"Message report:"):aprint(A.report())
		if output_prompt_to_file:_save_prompt_to_file(A,C)
		try:O=K(A)
		except Exception as J:
			aprint(f"Error generating file: {J}");import traceback as P;aprint(P.format_exc())
			with asection('Message content before error:'):_save_prompt_to_file(A,C);aprint(f"FAILED!!!");return f"Generation failed due to an error: {J}."
		Q=O[-1][0].get_content();B=Q.strip()
		if B.startswith('```markdown'):B=B[11:]
		if B.startswith('```md'):B=B[5:]
		if B.endswith('```'):B=B[:-3]
		os.makedirs(os.path.dirname(C),exist_ok=True)
		with open(C,'w')as R:R.write(B)
		return B
def _save_prompt_to_file(message,output_file):
	A=os.path.join(os.path.dirname(output_file),'debug_prompt.txt')
	with open(A,'w')as B:B.write(str(message))
```

==========
Prog. Lang. Code File: export_repo.py
Size: 2.16 kilobytes
==========

export_repo.py
```py
import os
from typing import Optional
from arbol import aprint,asection
from litemind.agent.messages.message import Message
from litemind.tools.commands.utils import default_folder_scanning_parameters
def export_repo(folder_path,allowed_extensions=None,excluded_files=None,output_file=None):
	D=folder_path;C=output_file;B=allowed_extensions;A=excluded_files
	with asection(f"Exporting entire repository in {D} to single file: {C}"):
		G=os.path.basename(C);B,A=default_folder_scanning_parameters(B,A);A.append(G);aprint(f"Excluded files: {", ".join(A)}");aprint(f"Allowed extensions: {", ".join(B)}");E=Message(role='user');E.append_folder(D,allowed_extensions=B,excluded_files=A);F=str(E)
		with open(C,'w')as H:H.write(F)
		return F
```

==========
Prog. Lang. Code File: scan.py
Size: 3.08 kilobytes
==========

scan.py
```py
_A=None
from typing import List,Optional,Type
from arbol import aprint,asection
from litemind.apis.base_api import BaseApi
from litemind.apis.feature_scanner import ModelFeatureScanner
def scan(apis,model_names=_A,output_dir=_A):
	D=apis;B=model_names
	if D is _A or not isinstance(D,list)or not all(isinstance(A,type)and issubclass(A,BaseApi)for A in D):aprint('No valid APIs provided');return
	for E in D:
		with asection(f"Scanning model(s) {B if B else"all"} from API: {E.__name__}"):
			F=ModelFeatureScanner(output_dir=output_dir);G=E()
			try:
				C=G.list_models()
				with asection(f"Found {len(C)} models:"):
					for H in C:aprint(f" - {H}")
			except Exception as I:aprint(f"Error listing models: {I}");C=[]
			if not C:aprint('No models available for scanning.');return
			if B is not _A:
				A=[A for A in C if A in B]
				if len(A)<len(B):J=set(B)-set(A);aprint(f"Warning: Some requested models are not available: {J}")
			else:A=C
			if not A:aprint('No models to scan.');return
			aprint(f"Scanning {len(A)} models: {", ".join(A)}");F.scan_apis([E],model_names=A);K=F.generate_markdown_report();L=F.save_results();aprint('\n'+K)
```

==========
Prog. Lang. Code File: utils.py
Size: 732 bytes
==========

utils.py
```py
import yaml
def default_folder_scanning_parameters(allowed_extensions,excluded_files):
	B=excluded_files;A=allowed_extensions
	if A is None:A=['.py','.md','.txt','.toml','LICENSE','.tests','.html']
	if B is None:B=['litemind.egg-info','dist','build']
	return A,B
def parse_yaml(file_path):
	with open(file_path,'r')as A:B=yaml.safe_load(A)
	return B
```




==========
Text File: demo.ipynb
Size: 23.93 kilobytes
==========

demo.ipynb
```ipynb
{
 "cells": [
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-05T20:40:17.876354Z",
     "start_time": "2025-05-05T20:40:08.867625Z"
    }
   },
   "source": [
    "from litemind import OpenAIApi\n",
    "from litemind.agent.agent import Agent\n",
    "\n",
    "# Initialize the OpenAI API\n",
    "api = OpenAIApi()\n",
    "\n",
    "# Create an agent\n",
    "agent = Agent(api=api, model_name=\"o3-high\")\n",
    "\n",
    "# Add a system message to guide the agent's behavior\n",
    "agent.append_system_message(\"You are a helpful assistant.\")\n",
    "\n",
    "# Ask a question\n",
    "response = agent(\"What is the capital of France?\")\n",
    "\n",
    "# Print the response\n",
    "print(\"Simple Agent Response:\", response)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mOpenAI API is available.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mOllama API is available.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mGemini API is available.\u001B[0m\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/miniconda3/envs/litemind/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mGemini API is available.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mFFmpeg is available.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m├╗\u001B[0m \u001B[38;2;244;162;97mCalling agent: 'Agent'\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAPI and model:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mAPI: OpenAIApi\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mModel: gpt-4.5-preview\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 47.21 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAvailable tools\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mNo tools available\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 18.84 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAvailable augmentations\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mNo augmentations available\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 15.97 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mLast message in conversation:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*user*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mWhat is the capital of France?\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 30.99 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mReponse:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*assistant*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mThe capital of France is Paris.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 70.81 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│┴«\u001B[0m\u001B[38;2;42;157;175m 878.09 milliseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│\u001B[0m\u001B[0m\n",
      "Simple Agent Response: [*assistant*:\n",
      "The capital of France is Paris.\n",
      "]\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "cell_type": "code",
   "id": "4247b552229782ba",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-02T22:17:41.612587Z",
     "start_time": "2025-05-02T22:17:39.389300Z"
    }
   },
   "source": [
    "from litemind import OpenAIApi\n",
    "from litemind.agent.agent import Agent\n",
    "from litemind.agent.tools.toolset import ToolSet\n",
    "from datetime import datetime\n",
    "\n",
    "\n",
    "# Define a function to get the current date\n",
    "def get_current_date() -> str:\n",
    "    \"\"\"\n",
    "    Fetch the current date\n",
    "    \"\"\"\n",
    "    return datetime.now().strftime(\"%Y-%m-%d\")\n",
    "\n",
    "\n",
    "# Initialize the OpenAI API\n",
    "api = OpenAIApi()\n",
    "\n",
    "# Create a toolset\n",
    "toolset = ToolSet()\n",
    "\n",
    "# Add the function tool to the toolset\n",
    "toolset.add_function_tool(get_current_date)\n",
    "\n",
    "# Create the agent, passing the toolset\n",
    "agent = Agent(api=api, toolset=toolset)\n",
    "\n",
    "# Add a system message\n",
    "agent.append_system_message(\"You are a helpful assistant.\")\n",
    "\n",
    "# Ask a question that requires the tool\n",
    "response = agent(\"What is the current date?\")\n",
    "\n",
    "# Print the response\n",
    "print(\"Agent with Tool Response:\", response)\n",
    "# Expected output:\n",
    "# Agent with Tool Response: [Message(role='assistant', content='The current date is 2024-03-08.')]"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m├╗\u001B[0m \u001B[38;2;244;162;97mCalling agent: 'Agent'\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAPI and model:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mAPI: OpenAIApi\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mModel: gpt-4.5-preview\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 36.95 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAvailable tools\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mget_current_date() % Fetch the current date\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 20.98 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAvailable augmentations\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mNo augmentations available\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 15.97 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mLast message in conversation:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*user*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mWhat is the current date?\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 30.04 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mExecuting tool 'get_current_date'\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mArguments: (), {}\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mResult: 2025-05-02\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 61.99 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mReponse:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*assistant*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mAction: get_current_date()\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*user*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mAction: get_current_date()=2025-05-02\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*assistant*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mThe current date is May 2, 2025.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 110.15 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│┴«\u001B[0m\u001B[38;2;42;157;175m 1.72 seconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│\u001B[0m\u001B[0m\n",
      "Agent with Tool Response: [*assistant*:\n",
      "Action: get_current_date()\n",
      ", *user*:\n",
      "Action: get_current_date()=2025-05-02\n",
      ", *assistant*:\n",
      "The current date is May 2, 2025.\n",
      "]\n"
     ]
    }
   ],
   "execution_count": 2
  },
  {
   "cell_type": "markdown",
   "id": "79d9037ec6b154d0",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "id": "78afb3879dba025a",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-02T22:17:54.449328Z",
     "start_time": "2025-05-02T22:17:47.842093Z"
    }
   },
   "source": [
    "from litemind import OpenAIApi\n",
    "from litemind.agent.agent import Agent\n",
    "from litemind.agent.tools.toolset import ToolSet\n",
    "from litemind.agent.augmentations.information.information import Information\n",
    "from litemind.agent.augmentations.vector_db.in_memory_vector_db import (\n",
    "    InMemoryVectorDatabase,\n",
    ")\n",
    "from litemind.media.types.media_text import Text\n",
    "\n",
    "\n",
    "# Define a function to get the current date\n",
    "def get_current_date() -> str:\n",
    "    from datetime import datetime\n",
    "\n",
    "    return datetime.now().strftime(\"%Y-%m-%d\")\n",
    "\n",
    "\n",
    "# Initialize the OpenAI API\n",
    "api = OpenAIApi()\n",
    "\n",
    "# Create a toolset\n",
    "toolset = ToolSet()\n",
    "\n",
    "# Add the function tool to the toolset\n",
    "toolset.add_function_tool(get_current_date, \"Fetch the current date\")\n",
    "\n",
    "# Create the agent\n",
    "agent = Agent(api=api, toolset=toolset)\n",
    "\n",
    "# Create vector database augmentation\n",
    "vector_augmentation = InMemoryVectorDatabase(name=\"test_augmentation\")\n",
    "\n",
    "# Add sample informations to the augmentation\n",
    "informations = [\n",
    "    Information(\n",
    "        Text(\n",
    "          \"Igor Bolupskisty was a German-born theoretical physicist who developed the theory of indelible unitarity.\"\n",
    "        ),\n",
    "        metadata={\"topic\": \"physics\", \"person\": \"Bolupskisty\"},\n",
    "    ),\n",
    "    Information(\n",
    "        Text(\n",
    "          \"The theory of indelible unitarity revolutionized our understanding of space, time and photons.\"\n",
    "        ),\n",
    "        metadata={\"topic\": \"physics\", \"concept\": \"unitarity\"},\n",
    "    ),\n",
    "    Information(\n",
    "        Text(\n",
    "          \"Quantum unitarity is a fundamental theory in physics that describes nature at the nano-atomic scale as it pertains to Pink Hamsters.\"\n",
    "        ),\n",
    "        metadata={\"topic\": \"physics\", \"concept\": \"quantum unitarity\"},\n",
    "    ),\n",
    "]\n",
    "\n",
    "# Add informations to the vector database\n",
    "vector_augmentation.add_informations(informations)\n",
    "\n",
    "# Add augmentation to agent\n",
    "agent.add_augmentation(vector_augmentation)\n",
    "\n",
    "# Add a system message\n",
    "agent.append_system_message(\"You are a helpful assistant.\")\n",
    "\n",
    "# Ask a question that requires the tool\n",
    "response = agent(\n",
    "    \"Tell me about Igor Bolupskisty's theory of indelible unitarity. Also, what is the current date?\"\n",
    ")\n",
    "\n",
    "# Print the response\n",
    "print(\"Agent with Tool and Augmentation Response:\", response)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mComputing embeddings for 3 informations\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m├╗\u001B[0m \u001B[38;2;244;162;97mCalling agent: 'Agent'\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAPI and model:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mAPI: OpenAIApi\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mModel: gpt-4.5-preview\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 35.05 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAvailable tools\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mget_current_date() % Fetch the current date\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 20.03 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAvailable augmentations\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mtest_augmentation\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 15.02 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mLast message in conversation:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*user*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mTell me about Igor Bolupskisty's theory of indelible unitarity. Also, what is the current date?\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 30.28 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mRetrieving relevant informations for query: 'Tell me about Igor Bolupskisty's theory of indelible unitarity. Also, what is the current date?\n",
      "...'\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├╗\u001B[0m \u001B[38;2;244;162;97mRetrieved 3 informations from augmentation set:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mDocument ID: 82aa859f-d961-4f8a-8f4e-0245dba79be0, Score: 0.7669\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mDocument ID: d9c6db65-2240-43dd-8743-9c63884d9ed0, Score: 0.6078\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mDocument ID: d6e9e8de-9653-4714-8317-1ce90602e780, Score: 0.4642\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││┴«\u001B[0m\u001B[38;2;42;157;175m 100.14 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 488.78 milliseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mAdded 3 informations to conversation, context message:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*user*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mAdditional context information:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143m--- Document 1 (Relevance: 0.7669) [Source: test_augmentation] ---\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mIgor Bolupskisty was a German-born theoretical physicist who developed the theory of indelible unitarity.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143m--- Document 2 (Relevance: 0.6078) [Source: test_augmentation] ---\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mThe theory of indelible unitarity revolutionized our understanding of space, time and photons.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143m--- Document 3 (Relevance: 0.4642) [Source: test_augmentation] ---\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mQuantum unitarity is a fundamental theory in physics that describes nature at the nano-atomic scale as it pertains to Pink Hamsters.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 175.00 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mExecuting tool 'get_current_date'\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mArguments: (), {}\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143mResult: 2025-05-02\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 116.83 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│├╗\u001B[0m \u001B[38;2;244;162;97mReponse:\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*assistant*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mAction: get_current_date()\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*user*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mAction: get_current_date()=2025-05-02\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││├\u001B[0m \u001B[0m\u001B[38;2;42;157;143m*assistant*:\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mIgor Bolupskisty was a German-born theoretical physicist known for developing the theory of indelible unitarity. This groundbreaking theory significantly advanced our understanding of space, time, and photons, reshaping fundamental concepts in physics.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│││\u001B[0m \u001B[0m\u001B[38;2;42;157;143mToday's date is May 2, 2025.\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││┴«\u001B[0m\u001B[38;2;42;157;175m 155.93 microseconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m││\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│┴«\u001B[0m\u001B[38;2;42;157;175m 4.11 seconds\u001B[0m\u001B[0m\n",
      "\u001B[38;2;42;157;143m\u001B[38;2;233;196;106m│\u001B[0m\u001B[0m\n",
      "Agent with Tool and Augmentation Response: [*assistant*:\n",
      "Action: get_current_date()\n",
      ", *user*:\n",
      "Action: get_current_date()=2025-05-02\n",
      ", *assistant*:\n",
      "Igor Bolupskisty was a German-born theoretical physicist known for developing the theory of indelible unitarity. This groundbreaking theory significantly advanced our understanding of space, time, and photons, reshaping fundamental concepts in physics.\n",
      "\n",
      "Today's date is May 2, 2025.\n",
      "]\n"
     ]
    }
   ],
   "execution_count": 3
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
```



Folder '/Users/loic.royer/workspace/python/litemind/test_reports' is empty.


Please generate a detailed, complete and informative README.md file without any preamble or postamble:

