oskar-agent 0.1.3
Oskar - package to create and confiuguration of conversasional agents based on OpenAI protocolo.
Requires Python: >=3.10

Below is the US English translation, preserving all formatting, sections, and code blocks.

---

# Oskar Agent

## Overview

Oskar is a multi-purpose conversational agent defined in `Oskar/agent.py`. It integrates with the OpenAI Responses API (default `gpt-5` models), includes an offline fallback when the key is not configured, and activates a dynamic set of tools (Python execution, file read/write, search, RAG via ChromaDB, subordinate agents, enterprise integrations, etc.). Configuration is done through `AgentConfig` (`Oskar/agent_config.py`), responsible for prompts, tools, knowledge bases, and analytical sources.

## Prerequisites

- Python 3.12 or higher.

- Dependencies listed in `requirements.txt`.

- Environment variables `OPENAI_API_KEY` (required for online use) and optional `OPENAI_BASE_URL`.

- To enable SerpAPI (`search_web_tool`), set `SERPAPI_API_KEY`.

- `keys.yaml` may store credentials if you do not want to export them manually.

## Quick Setup

```bash
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install -r requirements.txt

export OPENAI_API_KEY="your-key"
# Optional: export OPENAI_BASE_URL="https://api.openai.com/v1"
```

Without `OPENAI_API_KEY`, the agent remains functional, but responds in offline mode (`[offline mode] ...`).

## Essential Concepts of the `Oskar` Class

- `AgentConfig`: a dataclass with slots defining model, prompt, tools, knowledge sources (`knowledge_base`), files (`working_files`), and databases (`working_databases`).

- `Oskar.answer(question, **kwargs)`: generates a `message_id`, prepares the interpolated prompt, builds messages with history/attachments via `attached_files`, sets `reasoning_effort`, orchestrates the call to the Responses API (with tool loop and offline fallback), and aggregates generated artifacts before returning the full payload (text, metadata, files, and usage).

- `response_callback`: optional function called on every response with the full payload.

- `attached_files`: parameter accepting file paths (string or list) to attach content to the query.

## Detailed Class Reference

### `AgentConfig`

**Properties**

- `agent_id`: unique identifier of the agent; defaults to `agent_name` when omitted.

- `agent_name`: display name of the agent, used in prompts and history (default: `"Oskar"`).

- `model`: primary model used by the agent (default: `"gpt-5"`).

- `model_settings`: optional dictionary with additional parameters (e.g., `history_window_size`, temperature).

- `system_prompt`: custom system instructions; when omitted, a default message is generated using the agent’s name.

- `description`: short text describing the agent for listings or auxiliary prompts.

- `tools_names`: list of allowed tools in addition to defaults (calculator, date/time).

- `custom_tools`: dictionary containing external tools registered dynamically.

- `knowledge_base`: list of Chroma sources (`name`, `folder`, `collection`) used to build RAG retrievers.

- `working_files`: metadata for CSV/auxiliary files available during the session (name, description, path).

- `working_databases`: SQL database definitions that automatically generate CSVs (`name`, `description`, `connection_string`, `query`).

- `json_config`: `InitVar` used only in the constructor to hydrate the instance via dictionary.

**Methods**

- `to_json()`: serializes all public fields into a dictionary ready for persistence.

- `restore_from_json(agent_config)`: updates only attributes present in the received dictionary.

### `Oskar`

**Exposed properties and attributes**

- `agent_config`: active `AgentConfig` instance for the session.

- `input_data`: dictionary with variables interpolated in prompts.

- `session_id`: UUID for the current session (auto-generated when omitted).

- `session_name`: optional label used in persistence and reporting.

- `session_created_at` / `session_updated_at`: ISO timestamps for creation and last update.

- `working_folder`: base directory for generated files and artifacts.

- `is_verbose`: enables detailed logs when `True`.

- `tools`: dictionary of currently enabled tools (default, optional, and enterprise).

- `message_history`: structured list containing questions, responses, attachments, and token usage.

- `history_window_size`: number of user/agent message pairs kept in short-term context (default: `5`).

- `retrievers`: RAG collections loaded from `knowledge_base`.

- `response_callback`: optional function called after each consolidated response.

- `id`: read-only alias of the agent.

- `name`: agent display name.

- `description`: description of the agent, its purpose, and capabilities.

- `model`: returns the model name.

- `reasoning_effort`: defines the agent reasoning mode ("none" | "low" | "medium" | "high"), default: "none".

**Public methods**

- `__init__(..., exec_custom_tool_fn=None)`: instantiates the agent with active configuration and session, prepares tool factories (default, enterprise, and custom), sets callbacks and working directories, and calls `_setup_agent()` to build the OpenAI client, registries, and RAG retrievers.
  
  | Parameter                      | Description                                              | Default          |
  | ------------------------------ | -------------------------------------------------------- | ---------------- |
  | `agent_config`                 | Agent configuration (model, prompt, tools, RAG sources). | `AgentConfig()`  |
  | `input_data`                   | Auxiliary variables interpolated in prompts.             | `{}`             |
  | `session_id`                   | Session UUID; auto-generated when omitted.               | `uuid4()`        |
  | `session_name`                 | Session display name.                                    | `None`           |
  | `session_created_at`           | Session creation timestamp.                              | `datetime.now()` |
  | `session_updated_at`           | Last update timestamp.                                   | `datetime.now()` |
  | `working_folder`               | Base directory for `output/<session_id>`.                | `Path.cwd()`     |
  | `is_verbose`                   | Enables detailed logs.                                   | `False`          |
  | `response_callback`            | Optional callback after each consolidated response.      | `None`           |
  | `get_builtin_custom_tools_fn`  | Alternative factory for enterprise tools.                | `None`           |
  | `build_custom_tool_schemas_fn` | Additional builder for custom tool schemas.              | `None`           |
  | `exec_custom_tool_fn`          | Custom tool executor.                                    | `None`           |

- `to_json()`: exports `agent_config`, session metadata, derived state (tools, flags), and `message_history` into a dictionary ready for persistence or transport.
  
  | Parameter | Description                                          | Default |
  | --------- | ---------------------------------------------------- | ------- |
  | `-`       | Receives no arguments besides the instance (`self`). | `-`     |

- `from_json(data, working_folder)`: class method that reconstructs configuration, session timeline, and history from the snapshot returned by `to_json()`, using the provided `working_folder` to rehydrate attachments and outputs.
  
  | Parameter        | Description                                            | Default  |
  | ---------------- | ------------------------------------------------------ | -------- |
  | `data`           | Structure previously generated by `to_json()`.         | required |
  | `working_folder` | Base directory where restored outputs will be written. | required |

- `add_subordinated_agent(subordinate_agent, role=None)`: associates another Oskar instance as a subordinate collaborator, keeping a single agent per name, replicating working directory and verbosity, and optionally updating the description with the given role.
  
  | Parameter           | Description                                                    | Default  |
  | ------------------- | -------------------------------------------------------------- | -------- |
  | `subordinate_agent` | Subordinate agent instance to attach.                          | required |
  | `role`              | Role/description of the subordinate appended to `description`. | `None`   |

- `get_pretty_messages_history(message_format='raw', list_subordinated_agents_history=False)`: formats the history into blocks ready for visualization (`raw` or HTML), grouping question/answer pairs and optionally including subordinate interactions.
  
  | Parameter                          | Description                             | Default |
  | ---------------------------------- | --------------------------------------- | ------- |
  | `message_format`                   | Desired format (`raw` or `html`).       | `'raw'` |
  | `list_subordinated_agents_history` | Whether to include subordinate history. | `False` |

- `answer(question, message_format='raw', attached_files=None, model=None, reasoning_effort=None, action='chat', include_history=True, is_consult_prompt=False)`: prepares the prompt with session variables, ensures output folder, attaches files, sets reasoning effort, registers input in history, builds tool schemas, and executes up to three iterations with the Responses API (or offline mode), returning content, metadata, attachments, and token usage.
  
  | Parameter           | Description                                              | Default  |
  | ------------------- | -------------------------------------------------------- | -------- |
  | `question`          | User question or instruction.                            | required |
  | `message_format`    | Response format (`raw` or `html`).                       | `'raw'`  |
  | `attached_files`    | File path(s) attached to the prompt.                     | `None`   |
  | `model`             | Alternative model; defaults to `agent_config.model`.     | `None`   |
  | `reasoning_effort`  | Model reasoning level (`none`, `low`, `medium`, `high`). | `None`   |
  | `action`            | Functional label (e.g. `tool:calculator_tool`).          | `'chat'` |
  | `include_history`   | Includes history in the context window.                  | `True`   |
  | `is_consult_prompt` | Indicates internal consultation between agents.          | `False`  |

- `delete_old_files(max_age_days=30)`: removes aged files sent to the OpenAI API, returning a list of tuples (`id`, filename, creation date) for each removed item.

## Converters (`Oskar.converters`)

- `convert_csv_to_markdown_table(csv_data: str)`: converts raw CSV content (with header) into a Markdown table without an index column.
  
  | Parameter  | Description                         | Default  |
  | ---------- | ----------------------------------- | -------- |
  | `csv_data` | CSV text to be rendered as a table. | required |

- `convert_dict_to_markdown(data: dict, md_path: str)`: serializes a dictionary into Markdown and writes the result to the specified path.
  
  | Parameter | Description                | Default  |
  | --------- | -------------------------- | -------- |
  | `data`    | Data structure to convert. | required |
  | `md_path` | Output Markdown file path. | required |

- `convert_docx_to_markdown(docx_path: str, md_path: str, media_dir: str | None = None)`: converts DOCX to Markdown, exporting embedded media to the given folder.
  
  | Parameter   | Description                        | Default             |
  | ----------- | ---------------------------------- | ------------------- |
  | `docx_path` | Input `.docx` file path.           | required            |
  | `md_path`   | Path where Markdown will be saved. | required            |
  | `media_dir` | Directory for extracted images.    | parent of `md_path` |

- `convert_json_to_markdown(json_data: dict | list, doc_title: str = "Document")`: serializes dicts or lists into structured Markdown text.
  
  | Parameter   | Description                               | Default      |
  | ----------- | ----------------------------------------- | ------------ |
  | `json_data` | JSON structure (dict or list) to convert. | required     |
  | `doc_title` | Root title for generated headers.         | `"Document"` |

- `convert_json_to_csv(recs_json: list, filename: str)`: writes a list of dictionaries to CSV, cleaning line breaks and truncating long strings.
  
  | Parameter   | Description                | Default  |
  | ----------- | -------------------------- | -------- |
  | `recs_json` | Records composing the CSV. | required |
  | `filename`  | Output CSV file path.      | required |

- `convert_markdown_to_html(md_path: str, img_dir: str | None = None, insert_header: bool = True)`: generates HTML from a Markdown file, optionally adjusting image paths and including a default header.
  
  | Parameter       | Description                            | Default  |
  | --------------- | -------------------------------------- | -------- |
  | `md_path`       | Source Markdown file path.             | required |
  | `img_dir`       | Prefix applied to image `src`.         | `None`   |
  | `insert_header` | Inserts default `<head>` and `<body>`. | `True`   |

- `convert_markdown_to_html_block(text: str, flag_insert_copy_to_clipboard_command: bool = True)`: converts Markdown text to HTML and returns the list of detected code block languages.
  
  | Parameter                               | Description                        | Default  |
  | --------------------------------------- | ---------------------------------- | -------- |
  | `text`                                  | In-memory Markdown content.        | required |
  | `flag_insert_copy_to_clipboard_command` | Adds copy controls to code blocks. | `True`   |

- `convert_markdown_to_pdf(md_filename: str, img_dir: str)`: renders Markdown to PDF via `wkhtmltopdf`, reusing generated intermediate HTML.
  
  | Parameter  | Description                           | Default  |
  | ---------- | ------------------------------------- | -------- |
  | `md_path`  | Markdown input path.                  | required |
  | `pdf_path` | PDF output path.                      | required |
  | `img_dir`  | Directory resolving image references. | required |

- `convert_pdf_to_markdown(pdf_ptah: str, md_path: str | None = None)`: extracts text from a PDF, attempts to mark headings, and returns the resulting Markdown path.
  
  | Parameter  | Description              | Default          |
  | ---------- | ------------------------ | ---------------- |
  | `pdf_path` | Input PDF file path.     | required         |
  | `md_path`  | Generated Markdown path. | same name as PDF |

- `convert_pptx_to_markdown(pptx_path: str, md_path: str, media_dir: str | None = None)`: converts PPTX slides to Markdown, extracting text, tables, images, and charts to disk.
  
  | Parameter   | Description                | Default             |
  | ----------- | -------------------------- | ------------------- |
  | `pptx_path` | Input `.pptx` file path.   | required            |
  | `md_path`   | Output Markdown path.      | required            |
  | `media_dir` | Folder for exported media. | parent of `md_path` |

- `decode_file_from_str(encoded_data: str, out_path: str)`: decodes `b64:` or `b64+zlib:` encoded strings to a binary file on disk.
  
  | Parameter      | Description                           | Default  |
  | -------------- | ------------------------------------- | -------- |
  | `encoded_data` | Encoded content with expected prefix. | required |
  | `out_path`     | Output binary file path.              | required |

- `encode_file(pathname: str, compress: bool = True)`: reads a binary file and returns a JSON-safe string, optionally compressed via zlib.
  
  | Parameter  | Description                          | Default  |
  | ---------- | ------------------------------------ | -------- |
  | `pathname` | File to encode.                      | required |
  | `compress` | Enables compression prior to base64. | `True`   |

## Use Case Examples (`testes/`)

Scripts in `testes/` serve as complete recipes. All can be executed directly (`python testes/<script>.py`) after setting up dependencies.

### 1. Basics and Persistence

- `testes/1a_test_basico.py`: instantiates the agent, registers a usage callback and sends a simple question.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  ag_cfg = AgentConfig(model_settings={"history_window_size": 5})
  agent = Oskar(agent_config=ag_cfg, is_verbose=True)
  res = agent.answer("Who is the president of Brazil?")
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

- `testes/1b_test_history.py`: demonstrates `to_json()` and `Oskar.from_json(...)` to persist complete history and restore the session before the next question.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  ag_cfg = AgentConfig(
      model_settings={"history_window_size": 5},
      system_prompt="You are a helpful assistant named oskar_agent.",
  )
  agent = Oskar(agent_config=ag_cfg, is_verbose=True)
  r1 = agent.answer("My favorite color is blue. What is your favorite color?")
  snapshot = agent.to_json()
  agent2 = Oskar.from_json(snapshot)
  r2 = agent2.answer("What is my favorite color?")
  print(json.dumps(r2, indent=2, ensure_ascii=False))
  ```

### 2. Internal Tools

- `testes/2a_test_tool_python.py`: enables `execute_python_code_tool` so that the model can generate and execute pandas/matplotlib code.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  ag_cfg = AgentConfig(
      tools_names=["execute_python_code_tool"],
      system_prompt="Use execute_python_code_tool to run Python code.",
  )
  agent = Oskar(agent_config=ag_cfg, is_verbose=True)
  res = agent.answer(
      "Create and execute a bar chart with matplotlib using the execute_python_code_tool."
  )
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

- `testes/2b_test_tool_calculator.py`: enforces explicit use of `calculator_tool`, even calling the tool via `action='tool:calculator_tool'`.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  ag_cfg = AgentConfig(
      tools_names=["calculator_tool"],
      system_prompt=(
          "You are an assistant focused on calculations. Whenever there is a mathematical expression, "
          "use the 'calculator_tool'."
      ),
  )
  agent = Oskar(agent_config=ag_cfg, is_verbose=True)
  expression = "1024 + 12 + 1"
  question = f"Calculate the following expression using calculator_tool: {expression}"
  res = agent.answer(question, action="tool:calculator_tool")
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

- `testes/2c_test_savefile_tool.py`: adds `write_file_tool` to save artifacts on disk (for example, a PlantUML diagram). The agent is instructed to use the tool whenever requested.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  ag_cfg = AgentConfig(
      tools_names=["write_file_tool"],
      system_prompt=(
          "You are an agent named oskar_agent. When asked to save content, "
          "use the 'write_file_tool'."
      ),
      model_settings={"history_window_size": 5},
  )
  agent = Oskar(agent_config=ag_cfg, is_verbose=True)
  agent.answer("Generate a PlantUML diagram of an international phone regex.")
  res = agent.answer("Save the PlantUML diagram to a file.")
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

### 3. File Upload and Manipulation

- `testes/3a_test_upload_md.py`: uploads a Markdown file (`testes/sources/cristianismo.md`) so the agent can produce an objective summary. Uses `attached_files` with an absolute path.
  
  ```python
  from pathlib import Path
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  md_path = Path("sources/cristianismo.md").resolve()
  agent = Oskar(agent_config= AgentConfig(), is_verbose=True)
  result = agent.answer(
      question="Read the attached file and produce an objective summary in Portuguese.",
      attached_files=str(md_path),
  )
  print(result.get("content", ""))
  ```

- `testes/3b_test_upload_img.py`: attaches an image (`testes/sources/img_pent.png`) and requests a detailed description.
  
  ```python
  from pathlib import Path
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  img_path = (Path(__file__).parent / "sources" / "img_pent.png").resolve()
  agent = Oskar(agent_config=AgentConfig(), is_verbose=False)
  result = agent.answer(
      question="Describe the attached image in detail in Portuguese.",
      attached_files=str(img_path),
  )
  print(result.get("content", ""))
  ```

- `testes/3c_test_upload_pdf_compare.py`: sends two PDFs simultaneously and requests a comparative analysis. Demonstrates that `attached_files` accepts a list of paths.
  
  ```python
  from pathlib import Path
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  pdf1_path = Path("sources/GlobalThreatReport2024.pdf").resolve()
  pdf2_path = Path("sources/comptia-state-of-cybersecurity-2025.pdf").resolve()
  agent = Oskar(agent_config=AgentConfig(), is_verbose=True)
  result = agent.answer(
      question="Provide a comparative analysis of these two PDF documents in Portuguese.",
      attached_files=[str(pdf1_path), str(pdf2_path)],
  )
  print(result.get("content", ""))
  ```

### 4. Knowledge Retrieval (RAG)

- `testes/4_test_RAG.py`: enables a local Chroma source (`./testes/sources/vectorstore`) and explicitly calls `retriever_tool`.
  
  ```python
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  ag_cfg = AgentConfig(
      knowledge_base=[{"name": "psychology", "folder": "./sources/vectorstore", "collection": "local-rag"}],
  )
  agent = Oskar(agent_config=ag_cfg, is_verbose=True)
  agent.answer("How many sessions were held?", action="tool:retriever_tool")
  ```

### 5. Multi-Agent System (MAS)

- `testes/5_test_MAS.py`: creates an orchestrator agent and adds a subordinate with specific knowledge via `add_subordinate_agent`, enabling the `ask_to_agent_tool`.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  assist_cfg = AgentConfig(
      agent_id="AssistantOskar",
      agent_name="oskar_agent Assistant",
      system_prompt="You know all company employees.",
  )
  agent_assist = Oskar(agent_config=assist_cfg, is_verbose=True)
  
  boss_cfg = AgentConfig(agent_name="Boss", model_settings={"history_window_size": 5})
  agent_boss = Oskar(agent_config=boss_cfg, is_verbose=True)
  
  agent_boss.add_subordinate_agent(
      agent_assist,
      "Knows the company’s employees and their respective roles.",
  )
  res = agent_boss.answer("What is Jacques' role?")
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

### 6. Enterprise Integrations (`Oskar_cg_tools`)

- `testes/6a_test_cg_tool_Salesforce_OPO.py`: configures a sales persona and enables `get_salesforce_opportunity_info_tool` to query opportunities in Salesforce.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  from my_company_tools.agent_cg_tools import (
      build_cg_tool_schemas,
      exec_cg_tool,
      get_builtin_cg_tools,
  )
  
  system_prompt = "Act as an analyst specialized in Salesforce Sales Cloud."
  ag_cfg = AgentConfig(
      system_prompt=system_prompt,
      tools_names=["get_salesforce_opportunity_info_tool"],
      model_settings={"history_window_size": 5},
  )
  agent = Oskar(
      agent_config=ag_cfg,
      get_builtin_custom_tools_fn=get_builtin_cg_tools,
      build_custom_tool_schemas_fn=build_cg_tool_schemas,
      exec_custom_tool_fn=exec_cg_tool,
      is_verbose=True,
  )
  res = agent.answer("Show the timeline of opportunity OPO-ORIZON-2024-08-0001")
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

- `testes/6b_test_cg_tool_Salesforce_ITSM.py`: enables `get_salesforce_case_info_tool` focusing on IT support, requesting Markdown-formatted output.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  from my_company_tools.agent_cg_tools import (
      build_cg_tool_schemas,
      exec_cg_tool,
      get_builtin_cg_tools,
  )
  
  system_prompt = "Act as an analyst responsible for tickets in Salesforce Services Cloud."
  ag_cfg = AgentConfig(
      system_prompt=system_prompt,
      tools_names=["get_salesforce_case_info_tool"],
      model_settings={"history_window_size": 5},
  )
  agent = Oskar(
      agent_config=ag_cfg,
      get_builtin_custom_tools_fn=get_builtin_cg_tools,
      build_custom_tool_schemas_fn=build_cg_tool_schemas,
      exec_custom_tool_fn=exec_cg_tool,
      is_verbose=True,
  )
  res = agent.answer("Show the timeline of ticket 00042386")
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

- `testes/6c_test_custom_tool_SQL.py`: registers a custom SQL tool (`query_pessoas_tool`) via `AgentConfig.custom_tools`, illustrating parameterized filters.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  from my_company_tools.agent_cg_tools import (
      build_cg_tool_schemas,
      exec_cg_tool,
      get_builtin_cg_tools,
  )
  
  custom_tools_info = {
      "query_pessoas_tool": {
          "func_name": "search_database_tool",
          "description": "Searches person data by name.",
          "connection_string": "...",
          "title": "People",
          "queries": [
              {
                  "title": "People located (first 30)",
                  "query": "select top 10 CO.Name, CO.Email ... where ...",
              }
          ],
      }
  }
  ag_cfg = AgentConfig(custom_tools=custom_tools_info, model_settings={"history_window_size": 5})
  agent = Oskar(
      agent_config=ag_cfg,
      get_builtin_custom_tools_fn=get_builtin_cg_tools,
      build_custom_tool_schemas_fn=build_cg_tool_schemas,
      exec_custom_tool_fn=exec_cg_tool,
      is_verbose=True,
  )
  res = agent.answer('List in a table the people whose names match "José Carlos".')
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

- `testes/6d_test_custom_tool_DOC_SQL.py`: similar to the previous example, but builds multiple queries (ticket info and associated emails) and uses `input_data` to interpolate values in the prompt.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  from my_company_tools.agent_cg_tools import (
      build_cg_tool_schemas,
      exec_cg_tool,
      get_builtin_cg_tools,
  )
  
  custom_tools_info = {
      "get_support_case_info_tool": {
          "func_name": "query_database_tool",
          "description": "Queries details of an IT support case.",
          "connection_string": "...",
          "title": "ITSM Technical Support Case",
          "queries": [
              {"title": "Ticket", "query": "select top 1 CH.TicketNumber ... where CH.TicketNumber = '{KEY}'"},
              {"title": "Emails", "query": "select CH.TicketNumber, MAIL.Subject ... where CH.TicketNumber = '{KEY}'"},
          ],
      }
  }
  ag_cfg = AgentConfig(custom_tools=custom_tools_info, model_settings={"history_window_size": 5})
  agent = Oskar(
      agent_config=ag_cfg,
      input_data={"ticket": 42555},
      get_builtin_custom_tools_fn=get_builtin_cg_tools,
      build_custom_tool_schemas_fn=build_cg_tool_schemas,
      exec_custom_tool_fn=exec_cg_tool,
      is_verbose=True,
  )
  res = agent.answer("Provide a synthesis of ticket {ticket}.")
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

### 7. Analytics and BI

- `testes/7a_test_BI_CSV.py`: uses `working_files` pointing to `testes/sources/Basileia.csv` and requests an analysis with a chart generated by the Python tool.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  ag_cfg = AgentConfig(
      working_files=[
          {
              "name": "basel",
              "description": "Temperature data of the city of Basel",
              "pathname": "./sources/Basileia.csv",
          }
      ],
  )
  agent = Oskar(agent_config=ag_cfg, is_verbose=True)
  res = agent.answer(
      "Create a line chart showing the evolution of average temperature over the years."
  )
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

- `testes/7b_test_BI_SQL.py`: provisions a relational database via `working_databases`, generates CSV in the session folder, and performs data visualizations.
  
  ```python
  import json
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  ag_cfg = AgentConfig(
      working_databases=[
          {
              "name": "Tickets",
              "description": "Technical support ticket information",
              "connection_string": "Driver={ODBC Driver 17 for SQL Server};Server=CGSQL07;Database=DB_KPI;Uid=relkpi;Pwd=tele13",
              "query": "select top 100 CH.TicketNumber ...",
          }
      ],
  )
  agent = Oskar(agent_config=ag_cfg, is_verbose=True)
  res = agent.answer("Create a bar chart by Manufacturer.")
  print(json.dumps(res, indent=2, ensure_ascii=False))
  ```

### 8. Image Generation

- `testes/99_X_test_gen_image.py`: requests the agent to create an image; if the model does not return the attachment directly, the script tries to generate the file via the Images API. Images are saved to `testes/output/99_X_test_gen_image/`.
  
  ```python
  import json
  import os
  from openai import OpenAI
  from Oskar.agent import Oskar
  from Oskar.agent_config import AgentConfig
  
  ag_cfg = AgentConfig(
      model_settings={"history_window_size": 5},
      system_prompt="You are a creative assistant named oskar_agent.",
  )
  agent = Oskar(agent_config=ag_cfg, is_verbose=True)
  question = "Generate a stylized drawing of the moon on a dark background, minimalist."
  res = agent.answer(question)
  files = res.get("files") or []
  if not files:
      client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL"))
      img = client.images.generate(model="gpt-image-1", prompt=question, size="1024x1024")
      files = [{"name": "generated_moon.png", "content": img.data[0].b64_json}]
  ```

## Additional Resources

- `testes/output/`: examples of artifacts produced during execution.

- `testes/sources/`: supporting files (CSV, PDFs, images).

- `AGENTS.md`: detailed documentation of internal architecture.

## Best Practices

- Use `is_verbose=True` during development to monitor tool calls and token usage.

- Always instruct the agent in `system_prompt` when a tool must be prioritized or when specific policies apply.

- Remember to name files generated by responses using the pattern `<message_id>-description.ext`; the `answer` method collects these artifacts automatically.

With these examples, you can adapt the `Oskar` class to any workflow: customer service, BI, enterprise integrations, multi-agent systems, and multimodal artifact generation.

## Dependencies (`requirements.txt`)

The libraries below are versioned exactly as in `requirements.txt` and cover the minimum stack to run agents and tools.

### Core agent + Knowledge Base

```
openai==2.7.1
faiss-cpu==1.12.0
```

### Data tools used by `execute_python_code_tool`

```
pandas==2.3.0
numpy==2.3.4
seaborn==0.13.2
matplotlib==3.10.7
tabulate==0.9.0 (required for `DataFrame.to_markdown`)
```

### Document conversion and generation

```
pypdf==6.1.3
pdfkit==1.0.0 (requires `wkhtmltopdf` installed)
pdfminer.six==20250506
python-docx==1.2.0
markdown2==2.5.4
beautifulsoup4==4.14.2
```

### External integrations

```
simple-salesforce==1.12.9
```

### Misc utilities

```
pyodbc==5.3.0
PyYAML==6.0.3
colorama==0.4.6
```