## Quick Summary
The `utils` directory provides a collection of helper modules designed to support the core functionality of the agent. These utilities encapsulate common, reusable logic for tasks such as artifact management (saving, loading, schema inference), configuration parsing, and safe interaction with the ADK's invocation context.

## Files Overview
- `__init__.py` - An empty file that marks the directory as a Python package.
- `artifact_helpers.py` - Provides functions for creating, retrieving, and managing artifacts and their associated metadata.
- `config_parser.py` - Contains helpers for parsing and validating agent and app configurations.
- `context_helpers.py` - Offers utility functions for safely extracting data from ADK callback and invocation contexts.

## Developer API Reference

### artifact_helpers.py
**Purpose:** This module offers a comprehensive set of asynchronous functions for interacting with an artifact storage service. It handles saving artifacts with automatically generated metadata, inferring schemas for common data types, loading artifact content or just metadata, and listing available artifacts.
**Import:** `from src.solace_agent_mesh.agent.utils.artifact_helpers import is_filename_safe, ensure_correct_extension, save_artifact_with_metadata, load_artifact_content_or_metadata, get_latest_artifact_version, get_artifact_info_list, format_metadata_for_llm, decode_and_get_bytes`

**Functions:**
- `is_filename_safe(filename: str) -> bool` - Checks if a filename is safe for artifact creation (e.g., no path traversal).
- `ensure_correct_extension(filename_from_llm: str, desired_extension: str) -> str` - Corrects or adds the desired file extension to a filename.
- `save_artifact_with_metadata(artifact_service: BaseArtifactService, app_name: str, user_id: str, session_id: str, filename: str, content_bytes: bytes, mime_type: str, metadata_dict: Dict[str, Any], timestamp: datetime.datetime, explicit_schema: Optional[Dict] = None, schema_inference_depth: int = 2, schema_max_keys: int = 20, tool_context: Optional["ToolContext"] = None) -> Dict[str, Any]` - Asynchronously saves a data artifact and a corresponding metadata artifact, with optional schema inference.
- `format_metadata_for_llm(metadata: Dict[str, Any]) -> str` - Formats an artifact's metadata dictionary into a human-readable, LLM-friendly string.
- `decode_and_get_bytes(content_str: str, mime_type: str, log_identifier: str) -> Tuple[bytes, str]` - Decodes a string into bytes, handling base64 for binary MIME types and falling back to UTF-8 for text.
- `get_latest_artifact_version(artifact_service: BaseArtifactService, app_name: str, user_id: str, session_id: str, filename: str) -> Optional[int]` - Asynchronously retrieves the latest version number for a given artifact.
- `get_artifact_info_list(artifact_service: BaseArtifactService, app_name: str, user_id: str, session_id: str) -> List[ArtifactInfo]` - Asynchronously retrieves a list of detailed `ArtifactInfo` objects for all artifacts in the current context.
- `load_artifact_content_or_metadata(artifact_service: BaseArtifactService, app_name: str, user_id: str, session_id: str, filename: str, version: Union[int, str], load_metadata_only: bool = False, return_raw_bytes: bool = False, max_content_length: Optional[int] = None, component: Optional[Any] = None, encoding: str = "utf-8", error_handling: str = "strict") -> Dict[str, Any]` - Asynchronously loads an artifact's content or just its metadata for a specific version.

**Constants/Variables:**
- `METADATA_SUFFIX: str` - The suffix used for metadata files, `".metadata.json"`.
- `DEFAULT_SCHEMA_MAX_KEYS: int` - The default maximum number of keys to inspect when inferring a schema for a dictionary (default: 20).

**Usage Examples:**
```python
import asyncio
import datetime
from typing import Dict, Any, Optional, List, Union
from google.adk.artifacts import BaseArtifactService, MemoryArtifactService
from src.solace_agent_mesh.agent.utils.artifact_helpers import (
    save_artifact_with_metadata,
    load_artifact_content_or_metadata,
    get_artifact_info_list,
    ensure_correct_extension
)

# Assume 'service' is an instance of a BaseArtifactService implementation
service = MemoryArtifactService()
app_name = "my_app"
user_id = "user-1"
session_id = "session-1"

async def manage_artifacts():
    # 1. Ensure the filename from an LLM has the correct extension
    safe_filename = ensure_correct_extension("quarterly_report", "csv") # -> "quarterly_report.csv"

    # 2. Save an artifact with some metadata
    csv_content = b"id,name\n1,Alice\n2,Bob"
    save_result = await save_artifact_with_metadata(
        artifact_service=service,
        app_name=app_name,
        user_id=user_id,
        session_id=session_id,
        filename=safe_filename,
        content_bytes=csv_content,
        mime_type="text/csv",
        metadata_dict={"source": "manual_upload", "description": "User sales data"},
        timestamp=datetime.datetime.now(datetime.timezone.utc)
    )
    print(f"Save result: {save_result}")

    # 3. Load the content of the artifact we just saved
    loaded_artifact = await load_artifact_content_or_metadata(
        artifact_service=service,
        app_name=app_name,
        user_id=user_id,
        session_id=session_id,
        filename=safe_filename,
        version="latest"
    )
    if loaded_artifact.get("status") == "success":
        print(f"Loaded content: {loaded_artifact.get('content')}")

    # 4. List all available artifacts
    all_artifacts = await get_artifact_info_list(
        artifact_service=service,
        app_name=app_name,
        user_id=user_id,
        session_id=session_id
    )
    for artifact in all_artifacts:
        print(f"Found artifact: {artifact.filename} (v{artifact.version})")

# To run the async example
# asyncio.run(manage_artifacts())
```

### config_parser.py
**Purpose:** This module provides a utility to resolve configuration values that can be either a static string or a dynamic, callable "invoke" block. This is primarily used for parsing the agent's `instruction` configuration.
**Import:** `from src.solace_agent_mesh.agent.utils.config_parser import resolve_instruction_provider`

**Functions:**
- `resolve_instruction_provider(component, config_value: Any) -> Union[str, Callable[[ReadonlyContext], str]]` - Resolves an instruction from a configuration value. If the value is a string, it's returned directly. If it's a dictionary with an "invoke" key that resolves to a callable, the callable is returned.

**Usage Examples:**
```python
# Assume 'my_agent_component' is an instance of an agent component
# and it has a method 'get_config'

# Example 1: Config value is a simple string
config_str = "You are a helpful assistant."
instruction = resolve_instruction_provider(my_agent_component, config_str)
# instruction is "You are a helpful assistant."

# Example 2: Config value is a callable (e.g., from a YAML 'invoke' block)
def instruction_provider(context):
    return f"Assistant for user {context.user_id}"

instruction_func = resolve_instruction_provider(my_agent_component, instruction_provider)
# instruction_func is the instruction_provider function
```

### context_helpers.py
**Purpose:** This module provides safe and stable helper functions to extract information from the ADK's `CallbackContext` and other invocation context objects, abstracting away internal attribute access.
**Import:** `from src.solace_agent_mesh.agent.utils.context_helpers import get_session_from_callback_context, get_original_session_id`

**Functions:**
- `get_session_from_callback_context(callback_context: CallbackContext) -> Session` - Safely retrieves the persistent `Session` object from a `CallbackContext`.
- `get_original_session_id(invocation_context: Any) -> str` - Extracts the base session ID from a context, stripping any suffixes added after a colon (e.g., "session123:tool456" -> "session123").

**Usage Examples:**
```python
from google.adk.agents.callback_context import CallbackContext
from google.adk.sessions import Session, SessionId
from src.solace_agent_mesh.agent.utils.context_helpers import get_session_from_callback_context, get_original_session_id

# Mock context objects for demonstration
class MockInvocationContext:
    def __init__(self, session_id_str: str):
        self.session = Session(id=SessionId(session_id_str))

# 1. Get the full session object from a callback context
# In a real tool, 'callback_context' is provided by the ADK
mock_callback_context = CallbackContext(_invocation_context=MockInvocationContext("session123:tool456"))
session_obj = get_session_from_callback_context(mock_callback_context)
print(f"Full Session ID: {session_obj.id}") # -> "session123:tool456"

# 2. Get the original session ID from an invocation context
# In a real tool, this comes from 'tool_context._invocation_context'
mock_inv_context = MockInvocationContext("session123:tool456")
original_id = get_original_session_id(mock_inv_context)
print(f"Original Session ID: {original_id}") # -> "session123"

# Works with simple IDs too
mock_inv_context_simple = MockInvocationContext("session789")
original_id_simple = get_original_session_id(mock_inv_context_simple)
print(f"Original Session ID (simple): {original_id_simple}") # -> "session789