Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 5 additions & 94 deletions src/observability/README.md
Original file line number Diff line number Diff line change
@@ -1,97 +1,8 @@
# Observability Module
# List of source files stored in `src/observability` directory

This module provides telemetry capabilities for sending inference events to external systems like Splunk HEC.
## [__init__.py](__init__.py)
Observability module for telemetry and event collection.

## Architecture
## [splunk.py](splunk.py)
Async Splunk HEC client for sending telemetry events.

```
observability/
├── __init__.py # Public API exports
├── splunk.py # Async Splunk HEC client
└── formats/
├── __init__.py # Format exports
└── rlsapi.py # rlsapi v1 event format
```

## Usage

### Sending Events to Splunk

```python
from fastapi import BackgroundTasks
from observability import send_splunk_event, build_inference_event, InferenceEventData

# Build the event payload
event_data = InferenceEventData(
question="How do I configure SSH?",
response="To configure SSH...",
inference_time=2.34,
model="granite-3-8b-instruct",
org_id="12345678",
system_id="abc-def-123",
request_id="req_xyz789",
cla_version="CLA/0.6.0rc2",
system_os="RHEL",
system_version="9.3",
system_arch="x86_64",
)

event = build_inference_event(event_data)

# Queue for async sending via BackgroundTasks
background_tasks.add_task(send_splunk_event, event, "infer_with_llm")
```

### Source Types

| Source Type | Description |
|-------------|-------------|
| `infer_with_llm` | Successful inference requests |
| `infer_error` | Failed inference requests |

## Creating Custom Event Formats

To add a new event format for a different endpoint:

1. Create a new module in `observability/formats/`:

```python
# observability/formats/my_endpoint.py
from dataclasses import dataclass
from typing import Any

@dataclass
class MyEventData:
field1: str
field2: int

def build_my_event(data: MyEventData) -> dict[str, Any]:
return {
"field1": data.field1,
"field2": data.field2,
}
```

2. Export from `observability/formats/__init__.py`

3. Use with `send_splunk_event()`:

```python
from observability import send_splunk_event
from observability.formats.my_endpoint import build_my_event, MyEventData

event = build_my_event(MyEventData(field1="value", field2=42))
background_tasks.add_task(send_splunk_event, event, "my_sourcetype")
```

## Graceful Degradation

The Splunk client is designed to never block or fail the main request:

- Skips sending when Splunk is disabled or not configured
- Logs warnings on HTTP errors (does not raise exceptions)
- Token is read from file on each request (supports rotation without restart)

## Configuration

See [docs/splunk.md](../../docs/splunk.md) for configuration options.
94 changes: 94 additions & 0 deletions src/observability/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,100 @@
The splunk module provides a format-agnostic send_splunk_event() function.
Event formats are in the formats subpackage - see formats.rlsapi for the
default implementation, or create your own format module.

## Architecture

```
observability/
├── __init__.py # Public API exports
├── splunk.py # Async Splunk HEC client
└── formats/
├── __init__.py # Format exports
├── rlsapi.py # rlsapi v1 event format
└── responses.py # responses event format

## Usage

### Sending Events to Splunk

```python
from fastapi import BackgroundTasks
from observability import send_splunk_event, build_inference_event, InferenceEventData

# Build the event payload
event_data = InferenceEventData(
question="How do I configure SSH?",
response="To configure SSH...",
inference_time=2.34,
model="granite-3-8b-instruct",
org_id="12345678",
system_id="abc-def-123",
request_id="req_xyz789",
cla_version="CLA/0.6.0rc2",
system_os="RHEL",
system_version="9.3",
system_arch="x86_64",
)

event = build_inference_event(event_data)

# Queue for async sending via BackgroundTasks
background_tasks.add_task(send_splunk_event, event, "infer_with_llm")
```

### Source Types

| Source Type | Description |
|-------------|-------------|
| `infer_with_llm` | Successful inference requests |
| `infer_error` | Failed inference requests |

## Creating Custom Event Formats

To add a new event format for a different endpoint:

1. Create a new module in `observability/formats/`:

```python
# observability/formats/my_endpoint.py
from dataclasses import dataclass
from typing import Any

@dataclass
class MyEventData:
field1: str
field2: int

def build_my_event(data: MyEventData) -> dict[str, Any]:
return {
"field1": data.field1,
"field2": data.field2,
}
```

2. Export from `observability/formats/__init__.py`

3. Use with `send_splunk_event()`:

```python
from observability import send_splunk_event
from observability.formats.my_endpoint import build_my_event, MyEventData

event = build_my_event(MyEventData(field1="value", field2=42))
background_tasks.add_task(send_splunk_event, event, "my_sourcetype")
```

## Graceful Degradation

The Splunk client is designed to never block or fail the main request:

- Skips sending when Splunk is disabled or not configured
- Logs warnings on HTTP errors (does not raise exceptions)
- Token is read from file on each request (supports rotation without restart)

## Configuration

See [docs/splunk.md](../../docs/splunk.md) for configuration options.
"""

from observability.formats import (
Expand Down
2 changes: 1 addition & 1 deletion src/utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Utility function to dump schema with list of models into OpenAPI-compatible JSON
## [prompts.py](prompts.py)
Utility functions for system prompts.

## [pydantic_ai.py](pydantic_ai.py)
## [pydantic_ai_helpers.py](pydantic_ai_helpers.py)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a blank line before this heading.

This heading is directly adjacent to the preceding paragraph, violating Markdown rule MD022.

Proposed fix
 ## [prompts.py](prompts.py)
 Utility functions for system prompts.
 
+
 ## [pydantic_ai_helpers.py](pydantic_ai_helpers.py)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## [pydantic_ai_helpers.py](pydantic_ai_helpers.py)
## [pydantic_ai_helpers.py](pydantic_ai_helpers.py)
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 60-60: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/README.md` at line 60, Insert one blank line immediately before the
pydantic_ai_helpers.py heading in the README, leaving the heading text and
surrounding content unchanged.

Source: Linters/SAST tools

Helpers for running Pydantic AI agents against Llama Stack (Responses API compatibility).

## [query.py](query.py)
Expand Down
Loading