diff --git a/.agents/skills/probe/SKILL.md b/.agents/skills/probe/SKILL.md index 1dff141..6354304 100644 --- a/.agents/skills/probe/SKILL.md +++ b/.agents/skills/probe/SKILL.md @@ -1,11 +1,11 @@ --- name: probe -description: Diagnose what the agentops dashboard sees for a session or trace — empty Sessions page, missing trace, wrong user, missing attributes. Use when the user pastes a session/trace UUID, a `localhost:/sessions/` URL, or asks "why is this empty / why doesn't it show / what attributes are on this span". Trigger on bare UUIDs in the agentops repo without asking — the user expects a lookup. +description: Diagnose what the loupe dashboard sees for a session or trace — empty Sessions page, missing trace, wrong user, missing attributes. Use when the user pastes a session/trace UUID, a `localhost:/sessions/` URL, or asks "why is this empty / why doesn't it show / what attributes are on this span". Trigger on bare UUIDs in the loupe repo without asking — the user expects a lookup. --- -# Agentops probe +# loupe probe -This skill diagnoses what the agentops dashboard sees (or doesn't see) for a given session, trace, or the data stream as a whole. It's about debugging the _consumer side_ — "why is X empty / wrong / missing" — by comparing what the producer actually emitted against what agentops looks for. +This skill diagnoses what the loupe dashboard sees (or doesn't see) for a given session, trace, or the data stream as a whole. It's about debugging the _consumer side_ — "why is X empty / wrong / missing" — by comparing what the producer actually emitted against what loupe looks for. ## When this fires @@ -19,7 +19,7 @@ User says: - "this session doesn't appear" - "check session X" / "look at trace X" -If you're in the agentops repo and see a bare UUID, treat it as a session/trace id and look it up. Don't ask for confirmation. +If you're in the loupe repo and see a bare UUID, treat it as a session/trace id and look it up. Don't ask for confirmation. ## How to run it @@ -76,11 +76,11 @@ The JSON has this shape — focus on the diagnostic fields, not the timeline: | `trace_ids: []` (empty) | No data for this id. Wrong id, wrong env, or outside the time window (3d AI / 7d OO). | | `timeline` | Quick agent flow — invoke_agent, chat models, tool calls, purposes. Filters out generic HTTP / DB / queue spans by default. | | `errors` | Spans with `success=false`. Cite the span name. | -| `key_drift.sessionId` (multiple entries) | Same concept (`thread_id`) appears under multiple key names. App Insights customDimensions can carry both `ag_ui.thread_id` (dotted) and `ag_ui_thread_id` (underscore) depending on which SDK wrote it; agentops' `aiCoalesce` must check both forms via `bothForms()`. | -| `key_drift.session_only_underscore` | Trace has only underscore form, no dotted. If agentops looks for dotted-only, the trace won't appear on the Sessions page. | +| `key_drift.sessionId` (multiple entries) | Same concept (`thread_id`) appears under multiple key names. App Insights customDimensions can carry both `ag_ui.thread_id` (dotted) and `ag_ui_thread_id` (underscore) depending on which SDK wrote it; loupe' `aiCoalesce` must check both forms via `bothForms()`. | +| `key_drift.session_only_underscore` | Trace has only underscore form, no dotted. If loupe looks for dotted-only, the trace won't appear on the Sessions page. | | `purpose` field on a span | Standard key: `gen_ai.operation.purpose`. Legacy data may show `teammate.llm.purpose` (pre-refactor); new producer emits the standard key. | | `key_drift.purpose_on_ancestor_not_on_chat` | Purpose lives on parent Activity, not on the `chat` span. `propagateInheritedAttrs` lifts it down automatically for the standard key. | -| `key_drift.unrecognized_session_keys` / `unrecognized_purpose_keys` | Producer emitted these keys but agentops won't read them under current config. Either add to `conventions.ts` (if standard) or set the matching `CUSTOM_*_FIELD` env var. | +| `key_drift.unrecognized_session_keys` / `unrecognized_purpose_keys` | Producer emitted these keys but loupe won't read them under current config. Either add to `conventions.ts` (if standard) or set the matching `CUSTOM_*_FIELD` env var. | | `env_health` (per-session output) | Non-empty means a `CUSTOM_*_FIELD` env value contains chars that `field-config.ts` `ident()` silently drops (anything outside `[A-Za-z0-9_.]`). Fix the env value or relax the regex. | | `tokens` | Per-trace LLM token total. Useful for "why is this run so expensive". | @@ -90,8 +90,8 @@ The JSON has this shape — focus on the diagnostic fields, not the timeline: 1. Run `query.py --audit` first. 2. Check `env_health` — silent drops from `field-config.ts ident()` mean a `CUSTOM_*` override isn't taking effect even though it's set. -3. Check `emitted_keys_unrecognized_for_concept` — these are session/user/purpose keys the producer is emitting that agentops doesn't recognize. Top of that list is your fix target (add to `conventions.ts` or `CUSTOM_*_FIELD`). -4. Compare `traces_with_dotted` to `traces_with_only_underscore`. If underscore dominates, agentops' KQL coalesce is missing the underscore form — fix `aiCoalesce` in `src/lib/telemetry/conventions.ts` to run keys through `bothForms()`. +3. Check `emitted_keys_unrecognized_for_concept` — these are session/user/purpose keys the producer is emitting that loupe doesn't recognize. Top of that list is your fix target (add to `conventions.ts` or `CUSTOM_*_FIELD`). +4. Compare `traces_with_dotted` to `traces_with_only_underscore`. If underscore dominates, loupe' KQL coalesce is missing the underscore form — fix `aiCoalesce` in `src/lib/telemetry/conventions.ts` to run keys through `bothForms()`. 5. If `traces_in_listSessions_filter` is 0, the producer isn't emitting `gen_ai.operation.name`, `invoke_agent`, `execute_tool`, or `session.trigger_type` on any span — producer-side instrumentation issue. ### "This specific session/trace doesn't show" diff --git a/.agents/skills/probe/scripts/query.py b/.agents/skills/probe/scripts/query.py index 33ba9cd..a61966b 100644 --- a/.agents/skills/probe/scripts/query.py +++ b/.agents/skills/probe/scripts/query.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -Agentops debug query — pulls trace/session diagnostics from whichever -telemetry provider the agentops .env points at, and prints lean JSON. +loupe debug query — pulls trace/session diagnostics from whichever +telemetry provider the loupe .env points at, and prints lean JSON. Usage: query.py # per-session diagnostic @@ -25,8 +25,8 @@ from pathlib import Path from typing import Any -AGENTOPS_DIR = Path(__file__).resolve().parents[3].parent # skill is in agentops/.agents/skills/probe/scripts -ENV_FILE = AGENTOPS_DIR / ".env" +LOUPE_DIR = Path(__file__).resolve().parents[3].parent # skill is in loupe/.agents/skills/probe/scripts +ENV_FILE = LOUPE_DIR / ".env" # What we consider session/user attrs across all OTel/AG-UI/MAF variants. SESSION_KEYS = [ @@ -59,7 +59,7 @@ def load_env() -> dict[str, str]: - """Read agentops .env. Returns a dict; missing file = empty dict.""" + """Read loupe .env. Returns a dict; missing file = empty dict.""" env: dict[str, str] = {} if not ENV_FILE.exists(): return env @@ -77,7 +77,7 @@ def detect_provider(env: dict[str, str]) -> str: def recognized_keys(env: dict[str, str]) -> dict[str, set[str]]: - """Keys agentops will actually look at, given conventions.ts + .env overrides.""" + """Keys loupe will actually look at, given conventions.ts + .env overrides.""" sess = set(SESSION_KEYS) usr = set(USER_KEYS) purp = set(PURPOSE_KEYS) @@ -258,7 +258,7 @@ def diagnose_session_app_insights(env: dict[str, str], session_id: str, full: bo t["timeline"].append(entry_tl) t["span_count"] += 1 - # Detect "purpose tag on ancestor, not on the chat LLM span" — agentops + # Detect "purpose tag on ancestor, not on the chat LLM span" — loupe # propagateInheritedAttrs lifts it, but only if the key is recognized. def find_ancestor_purpose(span_id: str) -> str | None: cur = spans_by_id.get(span_id) @@ -288,12 +288,12 @@ def find_ancestor_purpose(span_id: str) -> str | None: drift = {} if len(sk) > 1: drift["sessionId"] = sk if len(pk) > 1: drift["purpose"] = pk - # Keys the producer emitted but agentops won't recognize given current config. + # Keys the producer emitted but loupe won't recognize given current config. unrec_sess = [k for k in sk if k not in recog["session"]] unrec_purp = [k for k in pk if k not in recog["purpose"]] if unrec_sess: drift["unrecognized_session_keys"] = unrec_sess if unrec_purp: drift["unrecognized_purpose_keys"] = unrec_purp - # Also flag agentops-style mismatch: only underscore form present, no dotted + # Also flag loupe-style mismatch: only underscore form present, no dotted all_dotted = [k for k in sk if "." in k] all_under = [k for k in sk if "_" in k and "." not in k] if all_under and not all_dotted: @@ -405,7 +405,7 @@ def audit_app_insights(env: dict[str, str]) -> dict[str, Any]: """ rows = query_app_insights(env, kql) # Distinct keys appearing in customDimensions on AI-relevant spans, grouped - # by whether agentops will look at them under current config. + # by whether loupe will look at them under current config. keys_kql = """ union dependencies, requests | where timestamp > ago(3d) @@ -428,7 +428,7 @@ def audit_app_insights(env: dict[str, str]) -> dict[str, Any]: all_recog = recog["session"] | recog["user"] | recog["purpose"] seen = [(r["k"], int(r.get("n") or 0)) for r in key_rows if r.get("k")] # Categorize: only flag a key as "unrecognized" if it LOOKS like a session/ - # user/purpose concept (the things agentops needs to match on) but isn't in + # user/purpose concept (the things loupe needs to match on) but isn't in # the recognized set. Generic OTel keys like gen_ai.request.model aren't # visibility-blocking and shouldn't show up as problems. def concept(k: str) -> str | None: diff --git a/.claude/skills/maf-sandbox/SKILL.md b/.claude/skills/maf-sandbox/SKILL.md index 8790a15..2f93d9c 100644 --- a/.claude/skills/maf-sandbox/SKILL.md +++ b/.claude/skills/maf-sandbox/SKILL.md @@ -1,11 +1,11 @@ --- name: maf-sandbox -description: Generate test telemetry for agentops by firing requests at a local Microsoft Agent Framework (MAF) Python agent that emits OTel spans to local OpenObserve. Use whenever the user wants to fire traces, produce spans, exercise telemetry shapes, generate test data for the agentops dashboard, verify how a particular pattern renders (parallel tool calls, subagent handoff, MCP tools, scheduled tasks, errors, streaming, token usage), or test the local OpenAI Responses endpoint — even if they don't say "MAF" or "sandbox" explicitly. Improvise the input each invocation; don't repeat payloads. Skip this skill when the user wants to *read* existing traces (that's the openobserve skill) or *diagnose* what agentops shows for a specific session id (that's the probe skill). +description: Generate test telemetry for loupe by firing requests at a local Microsoft Agent Framework (MAF) Python agent that emits OTel spans to local OpenObserve. Use whenever the user wants to fire traces, produce spans, exercise telemetry shapes, generate test data for the loupe dashboard, verify how a particular pattern renders (parallel tool calls, subagent handoff, MCP tools, scheduled tasks, errors, streaming, token usage), or test the local OpenAI Responses endpoint — even if they don't say "MAF" or "sandbox" explicitly. Improvise the input each invocation; don't repeat payloads. Skip this skill when the user wants to *read* existing traces (that's the openobserve skill) or *diagnose* what loupe shows for a specific session id (that's the probe skill). --- # MAF sandbox -Test rig for generating agent telemetry into local OpenObserve so we can inspect what agentops renders. +Test rig for generating agent telemetry into local OpenObserve so we can inspect what loupe renders. ## Quick start @@ -14,11 +14,11 @@ Test rig for generating agent telemetry into local OpenObserve so we can inspect ./fire.py "your prompt here" --stream # SSE stream ``` -`fire.py` handles everything: spawns `maf.py` via `uv` if not already running (logs → `/tmp/maf-sandbox.log`), discovers the entity_id, sends a correctly-shaped Responses API body, and returns the reply. The sandbox listens on `localhost:4280`, exports OTel to `http://localhost:5080/api/default` (OpenObserve), reads `OPENAI_API_KEY` from `agentops/.env.local`. +`fire.py` handles everything: spawns `maf.py` via `uv` if not already running (logs → `/tmp/maf-sandbox.log`), discovers the entity_id, sends a correctly-shaped Responses API body, and returns the reply. The sandbox listens on `localhost:4280`, exports OTel to `http://localhost:5080/api/default` (OpenObserve), reads `OPENAI_API_KEY` from `loupe/.env.local`. ## Optional: dual-emit to App Insights -agentops reads from App Insights by default — so to make sandbox traces visible in the agentops UI, also set `APPLICATIONINSIGHTS_CONNECTION_STRING` in `agentops/.env.local`. Without it, sandbox traces land only in OpenObserve and **agentops will not see them**; `maf.py`'s startup banner prints a warning in that case. AppInsights export is purely additive — OO emission continues either way. +loupe reads from App Insights by default — so to make sandbox traces visible in the loupe UI, also set `APPLICATIONINSIGHTS_CONNECTION_STRING` in `loupe/.env.local`. Without it, sandbox traces land only in OpenObserve and **loupe will not see them**; `maf.py`'s startup banner prints a warning in that case. AppInsights export is purely additive — OO emission continues either way. ## What the sandbox agent can do @@ -37,7 +37,7 @@ The agent (`sandbox-agent`) is wired to exercise these telemetry categories. **P 1. Fire a request: `./fire.py "..."` with an input chosen to exercise something interesting 2. Read the resulting spans via the `openobserve` skill, filtering `service_name=maf-sandbox` -3. Tell the user what attributes/shapes agentops would render — including anything missing, mangled, or that doesn't fit existing renderers +3. Tell the user what attributes/shapes loupe would render — including anything missing, mangled, or that doesn't fit existing renderers ## Files diff --git a/.claude/skills/maf-sandbox/maf.py b/.claude/skills/maf-sandbox/maf.py index 9bb0017..3da3330 100644 --- a/.claude/skills/maf-sandbox/maf.py +++ b/.claude/skills/maf-sandbox/maf.py @@ -23,7 +23,7 @@ from dotenv import load_dotenv -# Load OPENAI_API_KEY (and anything else) from the agentops repo .env.local — gitignored. +# Load OPENAI_API_KEY (and anything else) from the loupe repo .env.local — gitignored. _REPO_ROOT = Path(__file__).resolve().parents[3] load_dotenv(_REPO_ROOT / ".env.local") load_dotenv(_REPO_ROOT / ".env") @@ -59,7 +59,7 @@ configure_otel_providers(enable_sensitive_data=True) # Dual-emit to App Insights when configured — exercises the 8 KB -# customDimensions truncation that the agentops truncation-resilience branch +# customDimensions truncation that the loupe truncation-resilience branch # is meant to handle. Silently skipped when the connection string isn't set, # so the sandbox still works against OO alone. _AI_CONN = os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING") @@ -411,7 +411,7 @@ async def process(self, context: ChatContext, call_next: Any) -> None: print(f" OTel → {_ingest} (AppInsights)") else: print(" ⚠ AppInsights export OFF — set APPLICATIONINSIGHTS_CONNECTION_STRING in") - print(f" {_REPO_ROOT / '.env.local'} to enable. Without it, agentops") + print(f" {_REPO_ROOT / '.env.local'} to enable. Without it, loupe") print(" (which reads AppInsights by default) will NOT see these traces.") serve( entities=[main_agent, weather_subagent], diff --git a/.cta.json b/.cta.json index d7a512e..2810aba 100644 --- a/.cta.json +++ b/.cta.json @@ -1,5 +1,5 @@ { - "projectName": "agentops", + "projectName": "loupe", "mode": "file-router", "typescript": true, "packageManager": "npm", diff --git a/AGENTS.md b/AGENTS.md index 514c612..59feb6f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ -# agentops +# loupe -`TODO.md` is the running todo list. `docs/README.md` covers docs structure. **What attrs to emit / what agentops reads**: `docs/explanation/02-spec.md` is the canonical operating-set spec. +`TODO.md` is the running todo list. `docs/README.md` covers docs structure. **What attrs to emit / what loupe reads**: `docs/explanation/02-spec.md` is the canonical operating-set spec. ## Layout @@ -15,4 +15,4 @@ - `/traces` has two tabs: Traces (end-to-end runs; utility traces filtered out) and Spans (`?tab=spans`, lazy-fetched) listing utility purpose-attr spans + sub-agent invocations (`invoke_agent` under `execute_tool`). When the inspector is open, cmd+k narrows to spans in that session (`exclusive` provider in `use-span-search.tsx`). - Inspect drawer (`src/components/inspect/`, shared by sessions + traces): `drawer.tsx` Sheet shell · `overview.tsx` `InspectLayout` Spans-tab layout + inspector tabs · `tree.tsx` left pane (tree, palette) · `detail-panel.tsx` right pane (messages, tool calls, Make-prompt) · `context.tsx` Context-tab UI backed by pure `context-collectors.ts` · `context-segments.ts` stacked-bar math · `view-bar.tsx` `InspectViewBar`. Per-entity hosts that bind the right query live next to each route: `src/routes/sessions/-components/sessions-drawer-host.tsx`, `src/routes/traces/-components/trace-drawer-host.tsx`. Keep pure helpers in `.ts` siblings so tests don't pull `src/db` via React imports. - Span/domain helpers: `src/lib/spans.ts`. Formatting: `src/lib/format.ts`. -- Ingest: `src/lib/classify-span.ts` (OTel bag → typed `Classification`); deep dive at `docs/explanation/03-classify-span.md`. Provider clients in `src/lib/telemetry/` — no local mirror DB. Attribute reference: `docs/reference/ai-attributes.md` (full OTel catalog). Convention spec (curated subset agentops reads + stamps, including `gen_ai.task.*` and `tag.tags`): `docs/explanation/02-spec.md`. +- Ingest: `src/lib/classify-span.ts` (OTel bag → typed `Classification`); deep dive at `docs/explanation/03-classify-span.md`. Provider clients in `src/lib/telemetry/` — no local mirror DB. Attribute reference: `docs/reference/ai-attributes.md` (full OTel catalog). Convention spec (curated subset loupe reads + stamps, including `gen_ai.task.*` and `tag.tags`): `docs/explanation/02-spec.md`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 53c5f94..19a71e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,62 +1,62 @@ # Changelog -## [0.4.0](https://github.com/ivanrdvc/agentops/compare/agentops-v0.3.0...agentops-v0.4.0) (2026-05-26) +## [0.4.0](https://github.com/ivanrdvc/loupe/compare/agentops-v0.3.0...agentops-v0.4.0) (2026-05-26) ### Features -* maf-sandbox skill + inspect/home-chart polish ([#32](https://github.com/ivanrdvc/agentops/issues/32)) ([994f3a7](https://github.com/ivanrdvc/agentops/commit/994f3a738168f5d98fcf465bed7e9a0b6c876f96)) -* notes workflow + prompts foundation ([#29](https://github.com/ivanrdvc/agentops/issues/29)) ([a93447d](https://github.com/ivanrdvc/agentops/commit/a93447d3bf9c9813493645576ee78e954df22b12)) -* truncation resilience, inspect polish, and prompt/palette improvements ([#33](https://github.com/ivanrdvc/agentops/issues/33)) ([79f87a2](https://github.com/ivanrdvc/agentops/commit/79f87a2a57f0fcd31b9e07f5db6d4819404a1e12)) +* maf-sandbox skill + inspect/home-chart polish ([#32](https://github.com/ivanrdvc/loupe/issues/32)) ([994f3a7](https://github.com/ivanrdvc/loupe/commit/994f3a738168f5d98fcf465bed7e9a0b6c876f96)) +* notes workflow + prompts foundation ([#29](https://github.com/ivanrdvc/loupe/issues/29)) ([a93447d](https://github.com/ivanrdvc/loupe/commit/a93447d3bf9c9813493645576ee78e954df22b12)) +* truncation resilience, inspect polish, and prompt/palette improvements ([#33](https://github.com/ivanrdvc/loupe/issues/33)) ([79f87a2](https://github.com/ivanrdvc/loupe/commit/79f87a2a57f0fcd31b9e07f5db6d4819404a1e12)) ### Bug Fixes -* **prompts:** row filtering + reset state on navigation ([#31](https://github.com/ivanrdvc/agentops/issues/31)) ([1e70c29](https://github.com/ivanrdvc/agentops/commit/1e70c29fd6fceabbff2985f900764230eddc2005)) +* **prompts:** row filtering + reset state on navigation ([#31](https://github.com/ivanrdvc/loupe/issues/31)) ([1e70c29](https://github.com/ivanrdvc/loupe/commit/1e70c29fd6fceabbff2985f900764230eddc2005)) -## [0.3.0](https://github.com/ivanrdvc/agentops/compare/agentops-v0.2.0...agentops-v0.3.0) (2026-05-23) +## [0.3.0](https://github.com/ivanrdvc/loupe/compare/agentops-v0.2.0...agentops-v0.3.0) (2026-05-23) ### Features -* **changelog:** add changelog page from CHANGELOG.md ([#27](https://github.com/ivanrdvc/agentops/issues/27)) ([b13409d](https://github.com/ivanrdvc/agentops/commit/b13409d808cdd96a8e98f20afd18829afeaacf87)) -* **session-inspect:** real logs panel + shiki json + tasks scaffold ([#26](https://github.com/ivanrdvc/agentops/issues/26)) ([cf4d0d3](https://github.com/ivanrdvc/agentops/commit/cf4d0d35b8677bafba22571a0a7f9e86a74c0add)) -* **spans:** sub-agent rows + turn cost rollup ([#25](https://github.com/ivanrdvc/agentops/issues/25)) ([5e7f316](https://github.com/ivanrdvc/agentops/commit/5e7f316cd7a02a92e2820bd848e84ed306888da9)) -* **tasks:** build out tasks page + producer docs ([#28](https://github.com/ivanrdvc/agentops/issues/28)) ([d53214a](https://github.com/ivanrdvc/agentops/commit/d53214afbb5cfefec14f74fc3a0c43496d95dc72)) -* **theme:** neutral default + Slack/VS Code themes, tighter input text ([eac702a](https://github.com/ivanrdvc/agentops/commit/eac702a9f1ffd94300e0eba7550e39b47315202d)) +* **changelog:** add changelog page from CHANGELOG.md ([#27](https://github.com/ivanrdvc/loupe/issues/27)) ([b13409d](https://github.com/ivanrdvc/loupe/commit/b13409d808cdd96a8e98f20afd18829afeaacf87)) +* **session-inspect:** real logs panel + shiki json + tasks scaffold ([#26](https://github.com/ivanrdvc/loupe/issues/26)) ([cf4d0d3](https://github.com/ivanrdvc/loupe/commit/cf4d0d35b8677bafba22571a0a7f9e86a74c0add)) +* **spans:** sub-agent rows + turn cost rollup ([#25](https://github.com/ivanrdvc/loupe/issues/25)) ([5e7f316](https://github.com/ivanrdvc/loupe/commit/5e7f316cd7a02a92e2820bd848e84ed306888da9)) +* **tasks:** build out tasks page + producer docs ([#28](https://github.com/ivanrdvc/loupe/issues/28)) ([d53214a](https://github.com/ivanrdvc/loupe/commit/d53214afbb5cfefec14f74fc3a0c43496d95dc72)) +* **theme:** neutral default + Slack/VS Code themes, tighter input text ([eac702a](https://github.com/ivanrdvc/loupe/commit/eac702a9f1ffd94300e0eba7550e39b47315202d)) -## [0.2.0](https://github.com/ivanrdvc/agentops/compare/agentops-v0.1.1...agentops-v0.2.0) (2026-05-20) +## [0.2.0](https://github.com/ivanrdvc/loupe/compare/agentops-v0.1.1...agentops-v0.2.0) (2026-05-20) ### Features -* **shadcn:** migrate UI from Catalyst to shadcn ([#13](https://github.com/ivanrdvc/agentops/issues/13)) ([7716c1f](https://github.com/ivanrdvc/agentops/commit/7716c1f9540edae9ce16cb232d04cc7dfa00048d)) -* **traces:** traces view + home charts ([#18](https://github.com/ivanrdvc/agentops/issues/18)) ([4a740ba](https://github.com/ivanrdvc/agentops/commit/4a740ba16c18038962e1e7f22ab53477366aff18)) -* utility LLM classification, custom fields, session inspect polish ([#16](https://github.com/ivanrdvc/agentops/issues/16)) ([cb421e2](https://github.com/ivanrdvc/agentops/commit/cb421e214e20f53ccb7f64fa06cb278691e6136b)) -* workbench scaffolding (prompts + notes) ([#20](https://github.com/ivanrdvc/agentops/issues/20)) ([de54f0d](https://github.com/ivanrdvc/agentops/commit/de54f0d2ccfc3eb15d4c8aac2b39e65304a09674)) -* **workbench:** notes overhaul + inspect drawer redesign ([#22](https://github.com/ivanrdvc/agentops/issues/22)) ([eab3fb0](https://github.com/ivanrdvc/agentops/commit/eab3fb0dfc53a06710b205ddb4e74fa53e9fe159)) +* **shadcn:** migrate UI from Catalyst to shadcn ([#13](https://github.com/ivanrdvc/loupe/issues/13)) ([7716c1f](https://github.com/ivanrdvc/loupe/commit/7716c1f9540edae9ce16cb232d04cc7dfa00048d)) +* **traces:** traces view + home charts ([#18](https://github.com/ivanrdvc/loupe/issues/18)) ([4a740ba](https://github.com/ivanrdvc/loupe/commit/4a740ba16c18038962e1e7f22ab53477366aff18)) +* utility LLM classification, custom fields, session inspect polish ([#16](https://github.com/ivanrdvc/loupe/issues/16)) ([cb421e2](https://github.com/ivanrdvc/loupe/commit/cb421e214e20f53ccb7f64fa06cb278691e6136b)) +* workbench scaffolding (prompts + notes) ([#20](https://github.com/ivanrdvc/loupe/issues/20)) ([de54f0d](https://github.com/ivanrdvc/loupe/commit/de54f0d2ccfc3eb15d4c8aac2b39e65304a09674)) +* **workbench:** notes overhaul + inspect drawer redesign ([#22](https://github.com/ivanrdvc/loupe/issues/22)) ([eab3fb0](https://github.com/ivanrdvc/loupe/commit/eab3fb0dfc53a06710b205ddb4e74fa53e9fe159)) -## [0.1.1](https://github.com/ivanrdvc/agentops/compare/agentops-v0.1.0...agentops-v0.1.1) (2026-05-17) +## [0.1.1](https://github.com/ivanrdvc/loupe/compare/agentops-v0.1.0...agentops-v0.1.1) (2026-05-17) ### Features -* **session-inspect:** frontend tools, /docs skill, sessions table refactor ([#8](https://github.com/ivanrdvc/agentops/issues/8)) ([3715410](https://github.com/ivanrdvc/agentops/commit/37154109484d47a9c52aafffbed131d658d1a381)) +* **session-inspect:** frontend tools, /docs skill, sessions table refactor ([#8](https://github.com/ivanrdvc/loupe/issues/8)) ([3715410](https://github.com/ivanrdvc/loupe/commit/37154109484d47a9c52aafffbed131d658d1a381)) ## 0.1.0 (2026-05-17) ### Features -* build out app shell with sessions, live, inbox, mcp ([4f90c85](https://github.com/ivanrdvc/agentops/commit/4f90c85dc1b0c521907fad3fa8714af800ef2761)) -* runs viewer and project scaffolding ([1702a2b](https://github.com/ivanrdvc/agentops/commit/1702a2b1fcd0fc504acd4ee95eccb1dcfeea2e88)) -* sessions and observability UI refresh ([052be47](https://github.com/ivanrdvc/agentops/commit/052be47bb5cfd7b315d4a1021c92ac7f45fd531c)) -* sessions inventory, time-range filter, and trace drawer ([3d7cddf](https://github.com/ivanrdvc/agentops/commit/3d7cddf842203570b2487db5480ad921490e98a5)) -* sessions UI refresh + telemetry provider boundary ([f25ad4a](https://github.com/ivanrdvc/agentops/commit/f25ad4a837d067d2df8c7da02500f70d3e9e58d2)) -* sessions view, conversation builder, span classifier ([7441e79](https://github.com/ivanrdvc/agentops/commit/7441e7943939f52cc50d37ea2b87a19dd86b8719)) -* telemetry providers and runs list overhaul ([e8db11e](https://github.com/ivanrdvc/agentops/commit/e8db11e04f288618044b4d309c98969d1810d2d0)) +* build out app shell with sessions, live, inbox, mcp ([4f90c85](https://github.com/ivanrdvc/loupe/commit/4f90c85dc1b0c521907fad3fa8714af800ef2761)) +* runs viewer and project scaffolding ([1702a2b](https://github.com/ivanrdvc/loupe/commit/1702a2b1fcd0fc504acd4ee95eccb1dcfeea2e88)) +* sessions and observability UI refresh ([052be47](https://github.com/ivanrdvc/loupe/commit/052be47bb5cfd7b315d4a1021c92ac7f45fd531c)) +* sessions inventory, time-range filter, and trace drawer ([3d7cddf](https://github.com/ivanrdvc/loupe/commit/3d7cddf842203570b2487db5480ad921490e98a5)) +* sessions UI refresh + telemetry provider boundary ([f25ad4a](https://github.com/ivanrdvc/loupe/commit/f25ad4a837d067d2df8c7da02500f70d3e9e58d2)) +* sessions view, conversation builder, span classifier ([7441e79](https://github.com/ivanrdvc/loupe/commit/7441e7943939f52cc50d37ea2b87a19dd86b8719)) +* telemetry providers and runs list overhaul ([e8db11e](https://github.com/ivanrdvc/loupe/commit/e8db11e04f288618044b4d309c98969d1810d2d0)) ### Bug Fixes -* move token counting server-side, drop @anthropic-ai/tokenizer ([1746584](https://github.com/ivanrdvc/agentops/commit/17465845fd9e6c1cd150a67052de1d54ceabcb3e)) +* move token counting server-side, drop @anthropic-ai/tokenizer ([1746584](https://github.com/ivanrdvc/loupe/commit/17465845fd9e6c1cd150a67052de1d54ceabcb3e)) diff --git a/README.md b/README.md index 97e96f7..b8d77fa 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,35 @@ -# agentops +# loupe -[![CI](https://github.com/ivanrdvc/agentops/actions/workflows/ci.yml/badge.svg)](https://github.com/ivanrdvc/agentops/actions/workflows/ci.yml) -[![Release](https://img.shields.io/github/v/release/ivanrdvc/agentops?include_prereleases&sort=semver)](https://github.com/ivanrdvc/agentops/releases) +[![CI](https://github.com/ivanrdvc/loupe/actions/workflows/ci.yml/badge.svg)](https://github.com/ivanrdvc/loupe/actions/workflows/ci.yml) +[![Release](https://img.shields.io/github/v/release/ivanrdvc/loupe?include_prereleases&sort=semver)](https://github.com/ivanrdvc/loupe/releases) -Observability and monitoring for AI agents — traces, runs, evals, and MCP activity in one place. +Inspect AI agent traces, sessions, evals, and MCP activity in one local dashboard. -Requires Node 22 and pnpm 10. Run `pnpm install && pnpm dev` and open . See [`docs/`](docs/README.md) for architecture. +## Features + +- Sessions, traces, and spans +- **Inspector**: the central view. Conversation, context stack, span tree, tool calls, and live messages for any session or trace. +- Prompts and notes +- Evals +- MCP + +## Requirements + +- Node 22, pnpm 10 +- A telemetry backend that exposes OTel spans: + - [OpenObserve](https://openobserve.ai) (default; works zero-config against the Docker image) + - Azure Application Insights + +## Quickstart + +```bash +pnpm install +cp .env.example .env # edit if not using local OpenObserve +pnpm dev +``` + +Open http://localhost:3000. + +## Docs + +See [`docs/`](docs/README.md) for architecture, attribute reference, and design plans. diff --git a/TODO.md b/TODO.md index 972e137..aa50f34 100644 --- a/TODO.md +++ b/TODO.md @@ -6,8 +6,8 @@ - Compare two runs side-by-side — `docs/plans/compare-runs.md` - MCP — `docs/plans/mcp.md` - Prompt registry → trace linkage (revisit). Two paths discussed: span-attribute - convention (`agentops.prompt.name`, `agentops.prompt.version_hash` set by the - user's app, agentops links automatically on ingest) vs a C# SDK package that + convention (`loupe.prompt.name`, `loupe.prompt.version_hash` set by the + user's app, loupe links automatically on ingest) vs a C# SDK package that injects them. Code-as-source-of-truth preferred — don't move prompts out of `.cs` files. Park until after Playground + Notes ship. See `PLAYGROUND_PROMPTS_RESEARCH.md` for the rejected detection-by-fuzzy-match diff --git a/docs/README.md b/docs/README.md index d7253c1..19e4498 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # docs -In-repo engineering docs for agentops. +In-repo engineering docs for loupe. ## Map diff --git a/docs/_templates/explanation.md b/docs/_templates/explanation.md index e1106b8..7387622 100644 --- a/docs/_templates/explanation.md +++ b/docs/_templates/explanation.md @@ -6,7 +6,7 @@ summary: status: draft # draft | stable | deprecated owner: "@ivan" -audience: agentops-devs +audience: loupe-devs last-reviewed: YYYY-MM-DD # today tags: [] --- diff --git a/docs/_templates/guide.md b/docs/_templates/guide.md index c1121d6..37c1c99 100644 --- a/docs/_templates/guide.md +++ b/docs/_templates/guide.md @@ -4,7 +4,7 @@ type: guide summary: status: draft # draft | stable | deprecated owner: "@ivan" -audience: agentops-devs +audience: loupe-devs last-reviewed: YYYY-MM-DD # today tags: [] --- diff --git a/docs/explanation/01-architecture.md b/docs/explanation/01-architecture.md index e051f13..f67a645 100644 --- a/docs/explanation/01-architecture.md +++ b/docs/explanation/01-architecture.md @@ -1,22 +1,22 @@ --- title: Architecture type: explanation -summary: How agentops reads OTel traces, normalizes them through one classifier, +summary: How loupe reads OTel traces, normalizes them through one classifier, layers session / purpose / category / errors / sub-agent inference on top, and where every piece lives in the code. status: current owner: Ivan -audience: agentops-devs, AI assistants +audience: loupe-devs, AI assistants last-reviewed: 2026-05-25 tags: [architecture, ingest, classification, entry-point] --- # Architecture -agentops reads OTel traces emitted by agent frameworks, classifies the spans, +loupe reads OTel traces emitted by agent frameworks, classifies the spans, and renders agent activity. Read-only — no local telemetry mirror. -For the canonical list of attributes producers emit and agentops reads, see [`02-spec.md`](02-spec.md). +For the canonical list of attributes producers emit and loupe reads, see [`02-spec.md`](02-spec.md). ## Mental model @@ -38,7 +38,7 @@ For the canonical list of attributes producers emit and agentops reads, see [`02 └── orchestrate_tools ← producer-specific Activity ``` -Session is the only level agentops *adds*. Trace and Span are OTel. +Session is the only level loupe *adds*. Trace and Span are OTel. Span names shown above are the ones we recognize and route through the classifier; full vocabulary in [`../reference/ai-attributes.md`](../reference/ai-attributes.md). @@ -54,7 +54,7 @@ We do not instrument; we read. LangGraph, raw OpenAI/Anthropic …) │ OTel exporter │ ▼ ▼ - ─────────────────────────────────────────► agentops + ─────────────────────────────────────────► loupe producer's choice (this repo) ``` @@ -118,9 +118,9 @@ column names); they never decide *field meaning*. That decision lives in Span.rawAttributes full bag, for the raw-fields inspector ``` -## What agentops layers on top of OTel +## What loupe layers on top of OTel -These are agentops-specific concepts. OTel GenAI semconv defines none of them. The canonical convention list is in [`02-spec.md`](02-spec.md); the rules below are the consumer-side derivation when producers don't stamp the attrs themselves. +These are loupe-specific concepts. OTel GenAI semconv defines none of them. The canonical convention list is in [`02-spec.md`](02-spec.md); the rules below are the consumer-side derivation when producers don't stamp the attrs themselves. **Session** — roll-up of traces sharing a conversation id. @@ -273,7 +273,7 @@ Raw LLM calls — no agent framework involved. Some sessions are entirely this. ## Fallback inference rules -When producers don't stamp `gen_ai.task.parent.id` natively, agentops infers topology from span-tree shape. **Primary path is reading the stamped convention** ([`02-spec.md`](02-spec.md)); these rules run only as fallback. +When producers don't stamp `gen_ai.task.parent.id` natively, loupe infers topology from span-tree shape. **Primary path is reading the stamped convention** ([`02-spec.md`](02-spec.md)); these rules run only as fallback. One count grounds every rule: @@ -337,7 +337,7 @@ Rules: | Topic | Doc | | ----- | --- | -| Convention spec (what producers emit, what agentops reads) | [`02-spec.md`](02-spec.md) | +| Convention spec (what producers emit, what loupe reads) | [`02-spec.md`](02-spec.md) | | Classifier rules | [`03-classify-span.md`](03-classify-span.md) | | Sessions roll-up logic | [`sessions-vs-live.md`](sessions-vs-live.md) | | Attribute catalog (full OTel + extensions) | [`../reference/ai-attributes.md`](../reference/ai-attributes.md) | diff --git a/docs/explanation/02-spec.md b/docs/explanation/02-spec.md index b134d46..26ee035 100644 --- a/docs/explanation/02-spec.md +++ b/docs/explanation/02-spec.md @@ -1,22 +1,22 @@ --- -title: Agentops convention spec +title: loupe convention spec type: explanation -summary: The curated subset of OTel + extensions agentops operates on. - What producers emit, what agentops reads, what gets stamped consumer-side. +summary: The curated subset of OTel + extensions loupe operates on. + What producers emit, what loupe reads, what gets stamped consumer-side. status: stable owner: "@ivan" -audience: agent-instrumentation authors, agentops-devs +audience: agent-instrumentation authors, loupe-devs last-reviewed: 2026-05-24 tags: [convention, spec, ingest, attributes] --- -# Agentops convention spec +# loupe convention spec -The curated operating set. [`reference/ai-attributes.md`](../reference/ai-attributes.md) catalogs every attribute that exists in OTel / Logfire / OpenInference; this doc lists only what agentops actually reads, stamps, or expects producers to emit, with values and intent. +The curated operating set. [`reference/ai-attributes.md`](../reference/ai-attributes.md) catalogs every attribute that exists in OTel / Logfire / OpenInference; this doc lists only what loupe actually reads, stamps, or expects producers to emit, with values and intent. ## Decision -Adopt OTel GenAI semconv for per-call attributes and run-graph identity (`gen_ai.task.id` / `gen_ai.task.parent.id`). Keep agentops-defined `task.*` and `session.*` namespaces for scheduling identity (no overlap with OTel). Consumer-side normalisation fills in missing run-graph attrs from span-tree shape. +Adopt OTel GenAI semconv for per-call attributes and run-graph identity (`gen_ai.task.id` / `gen_ai.task.parent.id`). Keep loupe-defined `task.*` and `session.*` namespaces for scheduling identity (no overlap with OTel). Consumer-side normalisation fills in missing run-graph attrs from span-tree shape. No new vendor namespace. Where an existing convention covers a concept, use it. @@ -24,13 +24,13 @@ No new vendor namespace. Where an existing convention covers a concept, use it. | Concept | Attribute | Values | Source | Status | | ------- | --------- | ------ | ------ | ------ | -| Scheduling identity (what fires) | `task.id` | string | agentops convention | read | -| Scheduling kind | `task.kind` | `cron` \| `one_shot` \| `event` \| `webhook` | agentops convention | read | -| Schedule descriptor | `task.schedule` | cron expression or ISO timestamp | agentops convention | read | -| Human label | `task.name` | string | agentops convention | read | -| Origin (URL, source) | `task.source` | string | agentops convention | read | -| Trigger type | `session.trigger_type` | `scheduled` \| `event` \| `webhook` \| `user` | agentops convention | read | -| Async execution flag | `session.execution` | `background` | agentops convention | read (operational marker, not a task kind) | +| Scheduling identity (what fires) | `task.id` | string | loupe convention | read | +| Scheduling kind | `task.kind` | `cron` \| `one_shot` \| `event` \| `webhook` | loupe convention | read | +| Schedule descriptor | `task.schedule` | cron expression or ISO timestamp | loupe convention | read | +| Human label | `task.name` | string | loupe convention | read | +| Origin (URL, source) | `task.source` | string | loupe convention | read | +| Trigger type | `session.trigger_type` | `scheduled` \| `event` \| `webhook` \| `user` | loupe convention | read | +| Async execution flag | `session.execution` | `background` | loupe convention | read (operational marker, not a task kind) | | Session id (multi-turn) | `gen_ai.conversation.id` | string | OTel GenAI semconv | read | | AG-UI thread id | `ag_ui.thread_id` | string | AG-UI | read (alias for conversation.id) | | User id | `user.id` | string | OTel | read | @@ -78,7 +78,7 @@ Consumer-side normalisation at fetch time. The single pass in `src/lib/spans.ts` Pass-through: when a span arrives already carrying these attrs (Traceloop producers, LangGraph via the `graph.node.*` alias, anyone emitting them natively), the normaliser trusts the producer and skips its own stamping. -Why the work runs in agentops rather than producer-side or in an OTel Collector: OTTL transform processors are strictly per-span (can't dereference `parent_span_id` to walk ancestors), and we don't want to ship a producer-side SDK processor in two languages. Fallback inference rules + topology shapes: [`01-architecture.md`](01-architecture.md). +Why the work runs in loupe rather than producer-side or in an OTel Collector: OTTL transform processors are strictly per-span (can't dereference `parent_span_id` to walk ancestors), and we don't want to ship a producer-side SDK processor in two languages. Fallback inference rules + topology shapes: [`01-architecture.md`](01-architecture.md). ## Rendering diff --git a/docs/explanation/03-classify-span.md b/docs/explanation/03-classify-span.md index f0413d9..67bb6cd 100644 --- a/docs/explanation/03-classify-span.md +++ b/docs/explanation/03-classify-span.md @@ -6,7 +6,7 @@ summary: One function owns every rule for turning raw OTel attributes what it handles, and how to add a new provider. status: stable owner: "@ivan" -audience: agentops-devs +audience: loupe-devs last-reviewed: 2026-05-12 tags: [ingest, span-classification] --- diff --git a/docs/explanation/README.md b/docs/explanation/README.md index 9117647..25bd93f 100644 --- a/docs/explanation/README.md +++ b/docs/explanation/README.md @@ -1,17 +1,17 @@ # Explanation -The "why" of agentops subsystems. Mental models, architecture, trade-offs. +The "why" of loupe subsystems. Mental models, architecture, trade-offs. Longer-form prose. The highest-leverage section — if you can only write one doc, write it here. ## Read first (ordered) -- [01 — Architecture](01-architecture.md) — how agentops reads OTel traces, +- [01 — Architecture](01-architecture.md) — how loupe reads OTel traces, classifies spans, layers session / purpose / category / errors / sub-agent inference on top, and where every piece lives in the code. Includes the five trace topologies and the fallback inference rules. - [02 — Convention spec](02-spec.md) — the curated subset of OTel + extensions - agentops operates on. What producers emit, what agentops reads, what gets + loupe operates on. What producers emit, what loupe reads, what gets stamped consumer-side. Includes the per-category producer emission checklist. - [03 — Span classification](03-classify-span.md) — one function owns every @@ -20,7 +20,7 @@ doc, write it here. ## Subsystems -- [MCP read-through registry](mcp-read-through.md) — how agentops reads MCP +- [MCP read-through registry](mcp-read-through.md) — how loupe reads MCP registry references, fetches live server capabilities, and keeps SQLite limited to local app state. - [Sessions vs Runs](sessions-vs-live.md) — two top-level UI entries, two diff --git a/docs/explanation/mcp-read-through.md b/docs/explanation/mcp-read-through.md index 771d0e8..31e7496 100644 --- a/docs/explanation/mcp-read-through.md +++ b/docs/explanation/mcp-read-through.md @@ -1,7 +1,7 @@ --- title: MCP read-through registry type: explanation -summary: How agentops reads MCP registry references, fetches live server capabilities, and keeps SQLite limited to local app state. +summary: How loupe reads MCP registry references, fetches live server capabilities, and keeps SQLite limited to local app state. status: current owner: Ivan audience: engineers @@ -13,10 +13,10 @@ tags: [mcp, registry, telemetry, sqlite] `/mcp` is a live registry view, not a SQLite mirror. -The remote registry only gives agentops references to MCP servers. A reference +The remote registry only gives loupe references to MCP servers. A reference is enough to know that a server exists and how to reach it, but it is not the tool catalog. To render tool quality, counts, descriptions, and schemas, -agentops must call each referenced MCP server and ask for its live capabilities. +loupe must call each referenced MCP server and ask for its live capabilities. ## Ownership boundaries @@ -26,7 +26,7 @@ Three systems own different data: endpoint, transport, source metadata, and maybe owner metadata. - **MCP servers** own live capabilities: tools, descriptions, input schemas, and whatever else `tools/list` returns. -- **SQLite** owns agentops-local state: observed telemetry inventory, inbox +- **SQLite** owns loupe-local state: observed telemetry inventory, inbox alerts, user dismissals/snoozes, alert rules, and discovery cursors. Do not store `mcp_servers` or `mcp_tools` as canonical SQLite tables in v1. diff --git a/docs/explanation/prompts.md b/docs/explanation/prompts.md index 32d0e4f..af4db42 100644 --- a/docs/explanation/prompts.md +++ b/docs/explanation/prompts.md @@ -4,7 +4,7 @@ type: explanation summary: Folder tree of editable prompt snippets that run against a configured agent endpoint. status: draft owner: "@ivan" -audience: agentops-devs +audience: loupe-devs last-reviewed: 2026-05-24 tags: [prompts] --- diff --git a/docs/explanation/session-sidebar-recent.md b/docs/explanation/session-sidebar-recent.md index 79ae8cf..c2529dc 100644 --- a/docs/explanation/session-sidebar-recent.md +++ b/docs/explanation/session-sidebar-recent.md @@ -3,7 +3,7 @@ title: Session Sidebar Recent type: explanation summary: How the Sessions page and sidebar Recent list differ. status: current -owner: agentops +owner: loupe audience: engineers last-reviewed: 2026-05-15 tags: [sessions, sidebar, telemetry] @@ -21,7 +21,7 @@ comes from the current operator's identity and is applied server-side before returning up to five sessions. For now, identity is just a single user ID stored in browser `localStorage` -(key `agentops:user-id`). Set it from the UI: +(key `loupe:user-id`). Set it from the UI: 1. Open Settings → Account. 2. Enter your **User ID** — the same value emitted in telemetry as `user.id`, diff --git a/docs/explanation/tasks.md b/docs/explanation/tasks.md index 0904762..a4c0a8e 100644 --- a/docs/explanation/tasks.md +++ b/docs/explanation/tasks.md @@ -5,7 +5,7 @@ summary: What the Tasks page shows — machine-driven agent runs (scheduled, eve webhook, background) rolled up by task identity. status: draft owner: "@ivan" -audience: agentops-devs +audience: loupe-devs last-reviewed: 2026-05-23 tags: [tasks, otel, telemetry] --- @@ -14,7 +14,7 @@ tags: [tasks, otel, telemetry] A view over fires — individual executions of machine-driven agent runs. Each row in the table is a task definition; the count column is how many times it fired in the window. -Same posture as the rest of agentops: read-only over OTel, no local mirror, no provider-specific code. The Tasks page is a different query shape over the existing `TelemetryProvider`, not a new backend. +Same posture as the rest of loupe: read-only over OTel, no local mirror, no provider-specific code. The Tasks page is a different query shape over the existing `TelemetryProvider`, not a new backend. ## Where a fire comes from @@ -56,7 +56,7 @@ Tasks rows are scheduling-identity by definition (`task.*`); the run-graph attrs ### Identity priority (what becomes the row key) 1. **`task.id`** — primary key. One row per stable id. -2. **Root span operation name** — for fires emitted by cloud-native runtimes that don't stamp `task.id` (KEDA, Cloud Scheduler, etc.), the span name typically encodes the trigger source (e.g. `process queueitem`). agentops uses this as the cloud-semconv fallback identity. *Not a direct read of `cloud.scheduler.job.name` / `messaging.destination.name` / `http.route` today — those are the upstream attrs, but the span name is what reaches `TraceSummary.rootOperation`.* +2. **Root span operation name** — for fires emitted by cloud-native runtimes that don't stamp `task.id` (KEDA, Cloud Scheduler, etc.), the span name typically encodes the trigger source (e.g. `process queueitem`). loupe uses this as the cloud-semconv fallback identity. *Not a direct read of `cloud.scheduler.job.name` / `messaging.destination.name` / `http.route` today — those are the upstream attrs, but the span name is what reaches `TraceSummary.rootOperation`.* 3. **Derived `(service.name, gen_ai.agent.name, trigger_type)`** — last resort. Lossy: all fires sharing the same service+agent+trigger collapse into one row. Flagged with a `derived` badge so you know the rollup isn't authoritative. ## What the detail hero shows @@ -85,7 +85,7 @@ In short: the **Task** chip is the *schedule registration* (the cron/event subsc ## Non-goals - Storing task definitions locally. Definitions live in the observed app. -- Editing tasks from agentops (pause / cancel / re-run). Read-only. +- Editing tasks from loupe (pause / cancel / re-run). Read-only. - Parsing `task.schedule` cron expressions to compute exact expected-fire times. We use empirical median interval instead — works for cron, interval, and event-driven cadences with one path. - Cross-provider identity normalization beyond the priority order above. Apps that emit `cloud.scheduler.job.name` and apps that emit `task.id` show up as separate rows even if they're conceptually the same job. diff --git a/docs/plans/README.md b/docs/plans/README.md index 3518e47..e17612d 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -16,7 +16,7 @@ choices, immutable log). view them in a split layout that surfaces what diverged. - [Evals](evals.md) — ingestion shape for eval results plus the open questions on data model and UI. -- [HTTP API for LLM debugging](http-api.md) — expose agentops's +- [HTTP API for LLM debugging](http-api.md) — expose loupe's classification / reconstruction / aggregation views over plain endpoints so an LLM-driven dev tool can pull run data while a developer is debugging. - [MCP](mcp.md) — registry of MCP servers and tools so non-AI teams can ship diff --git a/docs/plans/agents.md b/docs/plans/agents.md index 720e4b6..c25fd95 100644 --- a/docs/plans/agents.md +++ b/docs/plans/agents.md @@ -38,7 +38,7 @@ The MCP registry can't see column 2; only an agent inventory can. The agent inve - **Workflows vs agents.** `/v1/entities` returns both. Each workflow has a non-empty `executors` field (nested nodes). v1 ingests them as agents (single `agents` row, type discriminator); rendering the executor tree is a follow-up. -- **OTel-derived agents (future).** Every distinct `gen_ai.agent.name` that appears as a top-level `invoke_agent` (no `gen_ai.task.parent.id`) is an agent — agentops can derive a second inventory from telemetry alone, alongside DevUI. Especially useful for non-MEAI frameworks. `gen_ai.task.parent.id` (per [`../explanation/02-spec.md`](../explanation/02-spec.md)) also gives sub-agent linkage without manual lint rules. +- **OTel-derived agents (future).** Every distinct `gen_ai.agent.name` that appears as a top-level `invoke_agent` (no `gen_ai.task.parent.id`) is an agent — loupe can derive a second inventory from telemetry alone, alongside DevUI. Especially useful for non-MEAI frameworks. `gen_ai.task.parent.id` (per [`../explanation/02-spec.md`](../explanation/02-spec.md)) also gives sub-agent linkage without manual lint rules. ## Source interface @@ -136,8 +136,8 @@ The agent test repo (`agent-run-test/agent/Program.cs:84`) mounts DevUI via `app | Endpoint | Method | What it gives | |---|---|---| | `/v1/entities` | GET | Agent/workflow inventory. Used here. | -| `/v1/responses` | POST | OpenAI-compatible Responses API. *Runs* the agent. Not in v1, but enables "send a test ping" from agentops. | -| `/v1/conversations` | GET/POST | OpenAI Conversations API. Thread state. Useful if we later want to mirror DevUI's chat UI inside agentops. | +| `/v1/responses` | POST | OpenAI-compatible Responses API. *Runs* the agent. Not in v1, but enables "send a test ping" from loupe. | +| `/v1/conversations` | GET/POST | OpenAI Conversations API. Thread state. Useful if we later want to mirror DevUI's chat UI inside loupe. | | `/devui/` | GET | The DevUI SPA itself. Linked from `/agents/$id` as an external link in v1. | | `/health` | GET | Liveness. Used for `/agents` "last seen" sanity-check. | @@ -155,7 +155,7 @@ There is no OpenAPI / Swagger spec, no per-entity detail endpoint (`/v1/entities ## Not in v1 -- Running agents from agentops (`POST /v1/responses` ping). +- Running agents from loupe (`POST /v1/responses` ping). - Conversation/thread mirroring. - Executor tree rendering for workflows. - Tool provenance — which MCP server an attached tool came from. diff --git a/docs/plans/evals.md b/docs/plans/evals.md index 2022e51..2bd4e3c 100644 --- a/docs/plans/evals.md +++ b/docs/plans/evals.md @@ -8,7 +8,7 @@ Status: draft. Ingestion shape is roughly settled (push + OTel + drop + manual). ### 1. Presentation & organization -How does a user think about "evals" inside agentops? Pick the mental model first; the schema falls out of it. +How does a user think about "evals" inside loupe? Pick the mental model first; the schema falls out of it. - **Flat list of runs, tagged?** Every ingested `ScenarioRunResult` is just another timestamped row; we filter by tag (`name=qa-bot-regression`, `env=ci`, `git_sha=…`). Cheapest. No "definition" concept at all — the eval *is* the set of runs that share a name. - **Definition + runs (two levels)?** A named eval definition (durable card) has a stream of runs underneath. Matches how Foundry / MEAI users think. Slightly more schema, much better landing page. @@ -53,7 +53,7 @@ Assume sessions from other users are already visible to us (per `docs/plans/sess Tentative compromise: **we orchestrate, we don't evaluate.** 1. User clicks "Run eval X against session Y" in the UI. -2. agentops POSTs the session messages + eval criteria to a **user-registered evaluator endpoint** (an HTTP webhook the user owns — could be their CI, a Lambda, an LLM-judge service). +2. loupe POSTs the session messages + eval criteria to a **user-registered evaluator endpoint** (an HTTP webhook the user owns — could be their CI, a Lambda, an LLM-judge service). 3. The evaluator runs wherever the user wants and POSTs results back to the existing `/api/evals/ingest`, tagged with the source session id. 4. The result row links to both the eval definition and the originating session. @@ -72,7 +72,7 @@ Sub-questions: ## Feature overview -Users run evals on their agents (MEAI in .NET, `agent_framework` in Python, or custom). agentops collects the results and shows them on an eval page with history and trace linkage. No outbound calls to Microsoft, no Foundry dependency. +Users run evals on their agents (MEAI in .NET, `agent_framework` in Python, or custom). loupe collects the results and shows them on an eval page with history and trace linkage. No outbound calls to Microsoft, no Foundry dependency. ## Ingestion — four paths, one landing zone @@ -86,11 +86,11 @@ Authorization: Bearer Body: ScenarioRunResult JSON (single or batched) ``` -Idempotent on `(project_id, definition_name, run.external_id)`. Callers: a `agentops-upload` CLI, a GitHub Action, or a tiny `@agentops/evals` SDK they import in their test setup. +Idempotent on `(project_id, definition_name, run.external_id)`. Callers: a `loupe-upload` CLI, a GitHub Action, or a tiny `@loupe/evals` SDK they import in their test setup. ### Path 2 — OTel piggyback (for users who already ship OTel to OpenObserve) -When this lands, the `eval.*` attrs below should be declared in [`../explanation/02-spec.md`](../explanation/02-spec.md) alongside the existing `gen_ai.*` and `task.*` sets — agentops's convention spec is the canonical home for "what attrs agentops reads", and eval attrs are a natural spec extension. Cross-link both directions when the spec gets a new section. +When this lands, the `eval.*` attrs below should be declared in [`../explanation/02-spec.md`](../explanation/02-spec.md) alongside the existing `gen_ai.*` and `task.*` sets — loupe's convention spec is the canonical home for "what attrs loupe reads", and eval attrs are a natural spec extension. Cross-link both directions when the spec gets a new section. Ship a small MEAI `IEvaluationResultStore` / Python equivalent that emits each result as an **OTel log record** with a known attribute schema: @@ -105,7 +105,7 @@ eval.metric.passed = true eval.metric.reason = "..." ``` -The log inherits the parent agent span's trace context → trace linkage is free. agentops queries OpenObserve for `event.name = "agentops.eval"` on a cron (or lazily) and materializes into the same tables as Path 1. +The log inherits the parent agent span's trace context → trace linkage is free. loupe queries OpenObserve for `event.name = "loupe.eval"` on a cron (or lazily) and materializes into the same tables as Path 1. ### Path 3 — Object-storage drop (no OTel, no outbound HTTP from CI) @@ -151,7 +151,7 @@ Indexes: `(definition_id, started_at desc)` for history; `(project_id, name)` fo ## Non-goals (v1) -- Authoring eval definitions inside agentops (we receive, we don't define). +- Authoring eval definitions inside loupe (we receive, we don't define). - Running evals ourselves / hosting evaluator LLMs. - Foundry integration (skipped per current direction). - Real-time streaming of a long-running eval. Batch on completion is fine. diff --git a/docs/plans/http-api.md b/docs/plans/http-api.md index 4a213dd..ac10988 100644 --- a/docs/plans/http-api.md +++ b/docs/plans/http-api.md @@ -1,6 +1,6 @@ # TODO — HTTP API for LLM debugging -agentops's value over raw OTel is the classification, conversation reconstruction, and aggregation we already do in `src/lib/`. The HTTP API exposes those same views over plain endpoints so an LLM-driven dev tool (Claude Code, Cursor, anything with a fetch) can pull run data while a developer is debugging — "why did my last run blow up" answered by the LLM itself, not by paste-and-pray. +loupe's value over raw OTel is the classification, conversation reconstruction, and aggregation we already do in `src/lib/`. The HTTP API exposes those same views over plain endpoints so an LLM-driven dev tool (Claude Code, Cursor, anything with a fetch) can pull run data while a developer is debugging — "why did my last run blow up" answered by the LLM itself, not by paste-and-pray. Localhost only, read-only, no auth. A Claude Code skill discovers it; the API itself stays transport-agnostic. Explicitly *not* MCP — protocol tax not justified for in-house local access, and we don't want to ship the bloat we're already planning to lint against in `mcp.md`. @@ -83,7 +83,7 @@ Search is **open** (see below). If included, it's: - [ ] `src/routes/api/runs/$runId/errors.ts` — errors-only markdown. - [ ] Verify dev + prod server binds `127.0.0.1` only. - [ ] `docs/reference/http-api.md` — endpoint reference table. -- [ ] Follow-up (separate task, not in this plan): Claude Code skill at `.claude/skills/agentops/SKILL.md` that triggers on debug-shaped questions and knows the endpoint URLs. +- [ ] Follow-up (separate task, not in this plan): Claude Code skill at `.claude/skills/loupe/SKILL.md` that triggers on debug-shaped questions and knows the endpoint URLs. ## Not in v1 diff --git a/docs/plans/mcp.md b/docs/plans/mcp.md index 192831b..4f48418 100644 --- a/docs/plans/mcp.md +++ b/docs/plans/mcp.md @@ -82,7 +82,7 @@ Thresholds are constants in `lint.ts`. Tune later. - Runtime health (errors, latency, unused tools). - Approving / blocking registrations. -- Editing the registry from agentops. +- Editing the registry from loupe. - Auto-fix for lint findings. - Slack/email alerting. - Snapshot-vs-snapshot compare view (could live under `/mcp/changes` later; v1 shows the delta from `first_seen` / `last_seen`). diff --git a/docs/plans/telemetry-backend.md b/docs/plans/telemetry-backend.md index 35948c7..df9c8e1 100644 --- a/docs/plans/telemetry-backend.md +++ b/docs/plans/telemetry-backend.md @@ -2,12 +2,12 @@ ## Problem -agentops currently reads spans from Azure Application Insights via its REST query API. At ~10k sessions/day with full LLM I/O in span attributes, interactive views (session list, trace tree, span detail) are too slow for production use. The cause is AI's query API + federated schema with JSON probing, not the underlying engine. +loupe currently reads spans from Azure Application Insights via its REST query API. At ~10k sessions/day with full LLM I/O in span attributes, interactive views (session list, trace tree, span detail) are too slow for production use. The cause is AI's query API + federated schema with JSON probing, not the underlying engine. We need a backend that: - accepts OTLP ingest from agents - supports fast keyed lookups (by `trace_id`, time range, session attributes) for span-tree and list queries -- feeds `classifySpan(name, attrs)` normalization in agentops via a new `TelemetryProvider` +- feeds `classifySpan(name, attrs)` normalization in loupe via a new `TelemetryProvider` - hosts on **Azure** - holds **6–12 months** retention - can be operated by a solo operator @@ -18,7 +18,7 @@ Volume estimate: 200k spans/day, ~10KB avg → ~400MB/day compressed → ~75GB a - License must be permissive (Apache 2.0 / MIT). No AGPL — rules out Grafana Tempo, OpenObserve OSS. - Must run on Azure infra. -- No local mirror in agentops itself; reads stay live via providers in `src/lib/telemetry/`. +- No local mirror in loupe itself; reads stay live via providers in `src/lib/telemetry/`. - Solo operator → ops burden must be light. ## Options considered @@ -34,18 +34,18 @@ Volume estimate: 200k spans/day, ~10KB avg → ~400MB/day compressed → ~75GB a - **Stack**: `clickhouse/clickhouse-server` on a B2ms VM (2 vCPU burst, 8GB, 256GB SSD) + `clickhouseexporter` in collector contrib - **License**: Apache 2.0 for both - **Cost**: $65–95/mo all-in -- **Pros**: cheapest columnar option; portable off Azure; B-series burst credits fit agentops's spiky query load; mature exporter (ClickHouse Inc maintained); `TTL ... DELETE` handles retention declaratively +- **Pros**: cheapest columnar option; portable off Azure; B-series burst credits fit loupe's spiky query load; mature exporter (ClickHouse Inc maintained); `TTL ... DELETE` handles retention declaratively - **Cons**: you own OS patches, CH upgrades, backup-to-Blob cron, disk-fill monitoring; single VM = no HA; manual config if you want hot/cold tiering to Blob at 12mo ### 3. Azure Database for PostgreSQL Flexible Server + JSONB - **Stack**: B1ms Postgres + thin collector receiver → Postgres writer (no first-class OTel Postgres trace exporter; ~50 lines of glue) - **License**: Postgres (PostgreSQL License, permissive) - **Cost**: $15–25/mo -- **Pros**: cheapest viable option; fully managed (backups, patches); familiar tooling; provider interface lets you swap to CH later without touching agentops +- **Pros**: cheapest viable option; fully managed (backups, patches); familiar tooling; provider interface lets you swap to CH later without touching loupe - **Cons**: not columnar — wide scans slow above ~10M rows (36M at 6mo is borderline); needs careful btree(`trace_id`) + GIN on attributes JSONB; no first-class OTel exporter means a small bespoke ingest service to maintain ### 4. Stay on Application Insights, hit ADX directly underneath -- **Stack**: keep AI ingest; bypass AI query API by cross-cluster-querying AI's underlying ADX from agentops +- **Stack**: keep AI ingest; bypass AI query API by cross-cluster-querying AI's underlying ADX from loupe - **Cost**: $0 incremental (already paying for AI) - **Pros**: no migration; cheapest possible path - **Cons**: AI's schema isn't tuned for trace-tree queries; partial perf win at best; doesn't solve the root cause @@ -60,5 +60,5 @@ Fall back to **Option 1 (ADX)** if ops-by-anyone-other-than-Ivan becomes a requi - What's the actual avg span size? LLM-heavy spans can be 50–200KB — re-do sizing with a real sample from current AI data before provisioning. - 6mo or 12mo retention? Default 6mo; extend later (ClickHouse `TTL` is a one-line change). -- Need HA? Single-VM CH has none. Acceptable for agentops? +- Need HA? Single-VM CH has none. Acceptable for loupe? - Cutover plan: dual-write OTLP for ~1–2 weeks, hard cut reads, decommission AI. diff --git a/docs/reference/README.md b/docs/reference/README.md index 66b4373..d2c60a3 100644 --- a/docs/reference/README.md +++ b/docs/reference/README.md @@ -9,10 +9,10 @@ wants to be in `explanation/` or `guides/` instead. - [AI / LLM trace attributes](ai-attributes.md) — OTel GenAI semconv plus the Logfire / OpenInference / vendor extensions seen in real traces. Lookup table for what each attribute key means when reading a span. -- [Telemetry providers](telemetry-providers.md) — how agentops reads spans +- [Telemetry providers](telemetry-providers.md) — how loupe reads spans from each backend (OpenObserve, Application Insights, …), the row → Span mapping, and the trace-scope post-processing every provider runs before returning data. - [Application Insights quirks](app-insights-quirks.md) — provider-specific - behaviors that bite the agentops read path (attribute truncation, duplicate + behaviors that bite the loupe read path (attribute truncation, duplicate ingestion) and the mitigations applied per file. diff --git a/docs/reference/ai-attributes.md b/docs/reference/ai-attributes.md index 33bd542..c9de543 100644 --- a/docs/reference/ai-attributes.md +++ b/docs/reference/ai-attributes.md @@ -34,7 +34,7 @@ Spec: Tag side-channel LLM calls (title gen, summarization, etc.) so the trace classifier buckets them as `utility` instead of `chat`. The span tree renders the value as a badge after the span name. -Not a published OTel semconv attribute — `gen_ai.*` is OTel's GenAI namespace but only `gen_ai.operation.name` is standardized. `gen_ai.operation.purpose` is a vendor-neutral extension we picked because (a) it sits in the right namespace and (b) it's the canonical key agentops reads. Deployments that already emit a different key set `CUSTOM_LLM_PURPOSE_FIELD=` and agentops picks it up alongside. +Not a published OTel semconv attribute — `gen_ai.*` is OTel's GenAI namespace but only `gen_ai.operation.name` is standardized. `gen_ai.operation.purpose` is a vendor-neutral extension we picked because (a) it sits in the right namespace and (b) it's the canonical key loupe reads. Deployments that already emit a different key set `CUSTOM_LLM_PURPOSE_FIELD=` and loupe picks it up alongside. `gen_ai.operation.name` is set by the SDK instrumentation (e.g. MEAI's `OpenTelemetryChatClient`) and drives span classification — do **not** override it. @@ -157,7 +157,7 @@ Stamped on the **root span** of a fire so the trace classifier and the Tasks pag Identity priority on the read side (for grouping fires into task rows): `task.id` → cloud semconv (`cloud.scheduler.job.name`, `messaging.destination.name`, `http.route`) → derived `(service.name, gen_ai.agent.name, trigger_type)` as a lossy fallback. -`task.*` is a vendor-neutral namespace defined by agentops — same posture as `gen_ai.operation.purpose`. Bare `task.*`, not `agentops.task.*`. +`task.*` is a vendor-neutral namespace defined by loupe — same posture as `gen_ai.operation.purpose`. Bare `task.*`, not `loupe.task.*`. ## AG-UI (CopilotKit) diff --git a/docs/reference/app-insights-quirks.md b/docs/reference/app-insights-quirks.md index b5d5ab1..e9be43a 100644 --- a/docs/reference/app-insights-quirks.md +++ b/docs/reference/app-insights-quirks.md @@ -2,7 +2,7 @@ title: Application Insights quirks type: reference summary: Provider-specific behaviors of Azure Monitor / Application Insights - that bite the agentops read path — attribute truncation, duplicate + that bite the loupe read path — attribute truncation, duplicate ingestion — and the mitigations applied per file. status: stable owner: "@ivan" diff --git a/docs/reference/telemetry-providers.md b/docs/reference/telemetry-providers.md index 474fad5..71437d7 100644 --- a/docs/reference/telemetry-providers.md +++ b/docs/reference/telemetry-providers.md @@ -1,7 +1,7 @@ --- title: Telemetry providers type: reference -summary: How agentops reads spans from each backend (OpenObserve, Application +summary: How loupe reads spans from each backend (OpenObserve, Application Insights, ...), the row → Span mapping, and the trace-scope post-processing every provider runs before returning data. status: stable @@ -13,7 +13,7 @@ tags: [telemetry, openobserve, app-insights, normalization] # Telemetry providers -agentops doesn't store telemetry. Every read goes through a provider in +loupe doesn't store telemetry. Every read goes through a provider in `src/lib/telemetry/` that translates the backend's row shape into the internal `Span` type. The rest of the codebase — drawers, trees, conversation views, query keys — only ever sees `Span[]`. Provider-specific knowledge stops at the diff --git a/package.json b/package.json index 70677e6..b67130c 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "agentops", + "name": "loupe", "version": "0.4.0", "private": true, "type": "module", diff --git a/public/favicon.ico b/public/favicon.ico index a11777c..fb98ad3 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg index 7d8331b..d508a80 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1,19 +1,5 @@ - - - + + + diff --git a/public/logo192.png b/public/logo192.png index fc44b0a..2974a2c 100644 Binary files a/public/logo192.png and b/public/logo192.png differ diff --git a/public/logo512.png b/public/logo512.png index a4e47a6..c3dc2cf 100644 Binary files a/public/logo512.png and b/public/logo512.png differ diff --git a/public/manifest.json b/public/manifest.json index 078ef50..c17e72e 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,12 +1,17 @@ { - "short_name": "TanStack App", - "name": "Create TanStack App Sample", + "short_name": "Loupe", + "name": "Loupe", "icons": [ { "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", + "sizes": "48x48 32x32 16x16", "type": "image/x-icon" }, + { + "src": "favicon.svg", + "type": "image/svg+xml", + "sizes": "any" + }, { "src": "logo192.png", "type": "image/png", @@ -20,6 +25,6 @@ ], "start_url": ".", "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" + "theme_color": "#18181b", + "background_color": "#18181b" } diff --git a/release-please-config.json b/release-please-config.json index b2e8601..6c24baa 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -3,7 +3,7 @@ "packages": { ".": { "release-type": "node", - "package-name": "agentops", + "package-name": "loupe", "include-v-in-tag": true, "bump-minor-pre-major": true, "bump-patch-for-minor-pre-major": false, diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index 9375fec..f56d959 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -69,7 +69,7 @@ export function AppSidebar() { - agentops + loupe {APP_VERSION} diff --git a/src/components/logo.tsx b/src/components/logo.tsx index 083c5b4..a0ae559 100644 --- a/src/components/logo.tsx +++ b/src/components/logo.tsx @@ -6,24 +6,12 @@ export function Logo({ className, ...props }: { className?: string } & React.Com - ) diff --git a/src/hooks/use-user.ts b/src/hooks/use-user.ts index a2145ef..d37f989 100644 --- a/src/hooks/use-user.ts +++ b/src/hooks/use-user.ts @@ -1,8 +1,8 @@ import { useCallback, useSyncExternalStore } from 'react' import { buildCurrentUser, type CurrentUser } from '#/lib/current-user' -const USER_ID_KEY = 'agentops:user-id' -const SCOPE_TO_ME_KEY = 'agentops:scope-to-me' +const USER_ID_KEY = 'loupe:user-id' +const SCOPE_TO_ME_KEY = 'loupe:scope-to-me' function makeStore(key: string) { const listeners = new Set<() => void>() diff --git a/src/lib/mcp/client.ts b/src/lib/mcp/client.ts index d773932..546cca4 100644 --- a/src/lib/mcp/client.ts +++ b/src/lib/mcp/client.ts @@ -52,7 +52,7 @@ async function postToolsList(endpoint: string): Promise { Accept: 'application/json, text/event-stream', 'Content-Type': 'application/json', }, - body: JSON.stringify({ jsonrpc: '2.0', id: 'agentops-tools-list', method: 'tools/list', params: {} }), + body: JSON.stringify({ jsonrpc: '2.0', id: 'loupe-tools-list', method: 'tools/list', params: {} }), signal: controller.signal, }) if (!resp.ok) throw new Error(`HTTP ${resp.status}`) diff --git a/src/lib/spans.ts b/src/lib/spans.ts index 86901d3..b87ee8d 100644 --- a/src/lib/spans.ts +++ b/src/lib/spans.ts @@ -59,7 +59,7 @@ export interface Span { agUiRunId?: string // Semantic purpose — e.g. "title_generation", "summarization". Set from // `gen_ai.operation.purpose`. Distinct from `gen_ai.operation.name` which - // names the OTel op (chat/invoke_agent/...). This one is the agentops + // names the OTel op (chat/invoke_agent/...). This one is the loupe // semantic tag (`title_generation`, `summarization`, ...). operationName?: string diff --git a/src/lib/tasks/rollup.ts b/src/lib/tasks/rollup.ts index 4f23e50..6b23b41 100644 --- a/src/lib/tasks/rollup.ts +++ b/src/lib/tasks/rollup.ts @@ -12,7 +12,7 @@ export interface TaskIdentity { // Resolve a trace's task identity in priority order: // 1. task.id — primary key, set by the app on the root span // 2. Cloud OTel semconv on rootOperation — cloud.scheduler.job.name, -// messaging.destination.name, http.route. agentops doesn't lift these +// messaging.destination.name, http.route. loupe doesn't lift these // into TraceSummary today; the rootOperation field already carries the // span name, which is what OO/AI emit for these (e.g. KEDA produces // `process queueitem`). Treated as the same family for grouping. diff --git a/src/lib/telemetry/conventions.ts b/src/lib/telemetry/conventions.ts index a065f3b..ff24c0f 100644 --- a/src/lib/telemetry/conventions.ts +++ b/src/lib/telemetry/conventions.ts @@ -1,5 +1,5 @@ // OTel attribute aliasing for fields where producers legitimately use -// different names (LLM tokens, cost, session id, model). The agentops +// different names (LLM tokens, cost, session id, model). The loupe // convention spec only pins agent-identity attrs; everything else stays // multi-alias so we can ingest from Langfuse / Pydantic / OpenLLMetry / // OpenInference / AG-UI without each producer having to conform. diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 5dd17bb..eb42bdb 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -35,7 +35,7 @@ export const Route = createRootRouteWithContext()({ content: 'width=device-width, initial-scale=1', }, { - title: 'agentops', + title: 'loupe', }, ], links: [ diff --git a/src/routes/changelog/-changelog-data.test.ts b/src/routes/changelog/-changelog-data.test.ts index aaf1999..ac9e997 100644 --- a/src/routes/changelog/-changelog-data.test.ts +++ b/src/routes/changelog/-changelog-data.test.ts @@ -3,25 +3,25 @@ import { parseChangelog } from './-changelog-data' const SAMPLE = `# Changelog -## [0.2.0](https://github.com/ivanrdvc/agentops/compare/agentops-v0.1.1...agentops-v0.2.0) (2026-05-20) +## [0.2.0](https://github.com/ivanrdvc/loupe/compare/loupe-v0.1.1...loupe-v0.2.0) (2026-05-20) ### Features -* **shadcn:** migrate UI from Catalyst to shadcn ([#13](https://github.com/ivanrdvc/agentops/issues/13)) ([7716c1f](https://github.com/ivanrdvc/agentops/commit/7716c1f9540edae9ce16cb232d04cc7dfa00048d)) -* **traces:** traces view + home charts ([#18](https://github.com/ivanrdvc/agentops/issues/18)) ([4a740ba](https://github.com/ivanrdvc/agentops/commit/4a740ba16c18038962e1e7f22ab53477366aff18)) +* **shadcn:** migrate UI from Catalyst to shadcn ([#13](https://github.com/ivanrdvc/loupe/issues/13)) ([7716c1f](https://github.com/ivanrdvc/loupe/commit/7716c1f9540edae9ce16cb232d04cc7dfa00048d)) +* **traces:** traces view + home charts ([#18](https://github.com/ivanrdvc/loupe/issues/18)) ([4a740ba](https://github.com/ivanrdvc/loupe/commit/4a740ba16c18038962e1e7f22ab53477366aff18)) ### Bug Fixes -* tighten span filter ([abc1234](https://github.com/ivanrdvc/agentops/commit/abc1234)) +* tighten span filter ([abc1234](https://github.com/ivanrdvc/loupe/commit/abc1234)) ## 0.1.0 (2026-05-17) ### Features -* initial release ([4f90c85](https://github.com/ivanrdvc/agentops/commit/4f90c85)) +* initial release ([4f90c85](https://github.com/ivanrdvc/loupe/commit/4f90c85)) ` describe('parseChangelog', () => { @@ -33,7 +33,7 @@ describe('parseChangelog', () => { it('captures the full compare url and date', () => { const [first] = parseChangelog(SAMPLE) expect(first.date).toBe('2026-05-20') - expect(first.url).toBe('https://github.com/ivanrdvc/agentops/compare/agentops-v0.1.1...agentops-v0.2.0') + expect(first.url).toBe('https://github.com/ivanrdvc/loupe/compare/loupe-v0.1.1...loupe-v0.2.0') }) it('handles plain header without compare url', () => {