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
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
db-up db-down docker-up docker-down docker-demo docker-logs \
init migrate demo ingest explain clusters ask \
api web web-serve worker test test-unit test-int test-cov lint format \
openapi client-go client-python clean
openapi jsonschema client-go client-python clean

PYTHON := python
PIP := pip
Expand Down Expand Up @@ -41,6 +41,7 @@ help:
@echo " make lint Ruff lint"
@echo " make format Ruff format"
@echo " make openapi Export OpenAPI schema to clients/openapi.json"
@echo " make jsonschema Export /v1/query JSON Schemas to clients/jsonschema/"
@echo " make client-go Generate Go client (oapi-codegen; no-op if missing)"
@echo " make client-python Optional OpenAPI Python generator, or use src/clients/v1.py"
@echo " make clean Remove build artifacts"
Expand Down Expand Up @@ -156,6 +157,9 @@ format:
openapi:
PYTHONPATH=. $(PYTHON) scripts/export_openapi.py

jsonschema:
PYTHONPATH=. $(PYTHON) scripts/export_jsonschema.py

# Generates clients/go/client.go when oapi-codegen is installed.
# Missing binary: print install hint and exit 0 so CI without Go still passes.
client-go: openapi
Expand Down
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@ Overlapping tail poll windows can **double-count** the same physical lines until

Unversioned `/ingestions`, `/query/*`, and `/config` remain as **deprecated aliases** for one release. They behave the same as the `/v1` paths and send `Deprecation: true` plus a `Link: </v1/...>; rel="successor-version"` header. `/health`, the web UI (`/`), and `/static` stay unversioned.

**Compatibility policy.** Additive changes stay in `v1`. Breaking path or method removals require `v2`. JSON response bodies are unchanged in this release (a stable evidence schema is tracked separately as G7).
**Compatibility policy.** Additive changes stay in `v1`. Breaking path or method removals require `v2`. `/v1/query/*` JSON bodies are **schema_version 1.0**: structured fields plus an `llm` provenance block (`used`, `provider`, `model`, `fell_back`). Prose is `rendered_text` (and `format: text` still adds a `text` alias; `format: markdown` still adds `markdown`). Additive fields may appear in 1.x; a breaking body change requires `schema_version` 2.0 / `v2`.

**OpenAPI and clients.** Export the spec with `make openapi` (`clients/openapi.json`). CI uploads that file as a workflow artifact and attaches it to GitHub Releases on tags. A thin typed Python client ships as `src.clients.v1.RaglogsClient` (targets `/v1`). `make client-go` runs [oapi-codegen](https://github.com/oapi-codegen/oapi-codegen) into `clients/go/` when the binary is installed; otherwise it prints the install command and exits 0. See `clients/README.md`.

Expand All @@ -1066,33 +1066,44 @@ curl -X POST http://localhost:8000/v1/query/explain \

```json
{
"window": {"start": "2026-03-12T22:00:00Z", "end": "2026-03-12T22:30:00Z"},
"summary": "Incident summary\n\nWindow: ...",
"confidence": "medium-high",
"mode": "rules",
"total_logs": 464,
"services_affected": ["api", "billing-worker"],
"schema_version": "1.0",
"scope": "default",
"window": {"from": "2026-03-12T22:00:00+00:00", "to": "2026-03-12T22:30:00+00:00"},
"confidence": {"label": "medium-high", "score": 0.72},
"summary": "Stripe signature verification failed for endpoint /webhooks/stripe",
"trigger": {"detected": false, "type": null, "service": null, "at": null, "correlation": null},
"primary_cluster": {
"message": "Stripe signature verification failed for endpoint /webhooks/stripe",
"fingerprint": "a1b2c3d4",
"template": "Stripe signature verification failed for endpoint /webhooks/stripe",
"count": 184,
"baseline_count": 0,
"change_ratio": 185.0
"change_ratio": 185.0,
"services": ["billing-worker"],
"levels": ["error"]
},
"evidence": ["184 similar errors in billing-worker", "..."]
"evidence": [
{"kind": "log", "detail": "184 similar errors in billing-worker"}
],
"llm": {"used": false, "provider": "disabled", "model": "gpt-4.1-mini", "fell_back": false},
"rendered_text": "Incident summary\n\nWindow: ...",
"cached": false,
"total_logs": 464
}
```

**Explain** — `POST /v1/query/explain` accepts the same window filters as the CLI. Optional `"format": "markdown"` adds a paste-ready `markdown` incident report field alongside the JSON payload (same shape as `raglogs explain --format markdown`).

**Timeline** — `POST /v1/query/timeline` accepts the same window filters as the CLI (`since` or `from_time`/`to_time`, optional `service`, `env`, `all_ingestions`, `ingestion_job_id`). Set `"format": "text"` to include a plain-text `text` field alongside `events`.
**Timeline** — `POST /v1/query/timeline` accepts the same window filters as the CLI (`since` or `from_time`/`to_time`, optional `service`, `env`, `all_ingestions`, `ingestion_job_id`). Set `"format": "text"` to include plain-text `rendered_text` (and a `text` alias) alongside `events`. Timeline is rules-only (`llm.used` is always false).

```bash
curl -X POST http://localhost:8000/v1/query/timeline \
-H "Content-Type: application/json" \
-d '{"since": "2h", "format": "json"}'
```

**Compare** — `POST /v1/query/compare` matches `raglogs compare`: either `"since"` + `"baseline"` (durations, window A ends at request time) or explicit `window_a_from` / `window_a_to` / `window_b_from` / `window_b_to`. Optional `"format": "text"` adds a rendered `text` field.
**Compare** — `POST /v1/query/compare` matches `raglogs compare`: either `"since"` + `"baseline"` (durations, window A ends at request time) or explicit `window_a_from` / `window_a_to` / `window_b_from` / `window_b_to`. Optional `"format": "text"` adds `rendered_text` (and a `text` alias). Cluster diffs include a `marker` (`+` / `-` / `↑` / `↓`; triggers use `+⚡` / `-⚡`). Compare is rules-only.

Published JSON Schema files live in `clients/jsonschema/` (`explain.v1.json`, `timeline.v1.json`, `compare.v1.json`, `ask.v1.json`). Export with `make jsonschema`.

```bash
curl -X POST http://localhost:8000/v1/query/compare \
Expand Down
9 changes: 9 additions & 0 deletions clients/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ make openapi
`clients/openapi.json` is the published contract. CI uploads it as a workflow
artifact on every push/PR and attaches it to GitHub Releases on tags.

## JSON Schema (`/v1/query/*`)

Versioned response bodies (`schema_version` 1.0) are published as JSON Schema:

```bash
make jsonschema
# writes clients/jsonschema/explain.v1.json (and timeline/compare/ask/clusters)
```

## Python

A thin typed httpx client ships in-tree:
Expand Down
106 changes: 106 additions & 0 deletions clients/jsonschema/ask.v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"$defs": {
"LlmProvenance": {
"properties": {
"used": {
"title": "Used",
"type": "boolean"
},
"provider": {
"title": "Provider",
"type": "string"
},
"model": {
"title": "Model",
"type": "string"
},
"fell_back": {
"title": "Fell Back",
"type": "boolean"
}
},
"required": [
"used",
"provider",
"model",
"fell_back"
],
"title": "LlmProvenance",
"type": "object"
}
},
"properties": {
"schema_version": {
"default": "1.0",
"title": "Schema Version",
"type": "string"
},
"question": {
"title": "Question",
"type": "string"
},
"answer": {
"title": "Answer",
"type": "string"
},
"evidence": {
"items": {
"type": "string"
},
"title": "Evidence",
"type": "array"
},
"clusters": {
"items": {
"additionalProperties": true,
"type": "object"
},
"title": "Clusters",
"type": "array"
},
"total_matches": {
"default": 0,
"title": "Total Matches",
"type": "integer"
},
"retrieval_mode": {
"default": "keyword",
"title": "Retrieval Mode",
"type": "string"
},
"llm": {
"$ref": "#/$defs/LlmProvenance"
},
"rendered_text": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Rendered Text"
},
"mode": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Mode"
}
},
"required": [
"question",
"answer",
"llm"
],
"title": "AskResponse",
"type": "object"
}
95 changes: 95 additions & 0 deletions clients/jsonschema/clusters.v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{
"$defs": {
"LlmProvenance": {
"properties": {
"used": {
"title": "Used",
"type": "boolean"
},
"provider": {
"title": "Provider",
"type": "string"
},
"model": {
"title": "Model",
"type": "string"
},
"fell_back": {
"title": "Fell Back",
"type": "boolean"
}
},
"required": [
"used",
"provider",
"model",
"fell_back"
],
"title": "LlmProvenance",
"type": "object"
},
"TimeWindow": {
"description": "Inclusive analysis window. Serialized as ``from`` / ``to`` (ISO-8601).",
"properties": {
"from": {
"title": "From",
"type": "string"
},
"to": {
"title": "To",
"type": "string"
}
},
"required": [
"from",
"to"
],
"title": "TimeWindow",
"type": "object"
}
},
"properties": {
"schema_version": {
"default": "1.0",
"title": "Schema Version",
"type": "string"
},
"scope": {
"default": "default",
"title": "Scope",
"type": "string"
},
"window": {
"$ref": "#/$defs/TimeWindow"
},
"clusters": {
"items": {
"additionalProperties": true,
"type": "object"
},
"title": "Clusters",
"type": "array"
},
"llm": {
"$ref": "#/$defs/LlmProvenance"
},
"ingestion_job_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Ingestion Job Id"
}
},
"required": [
"window",
"llm"
],
"title": "ClustersResponse",
"type": "object"
}
Loading
Loading