@agenthippo-ai/agenthippo 0.2.3-dataplane → 1.110.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/analytics/dashboard/provider.js +13 -2
- package/out/analytics/dashboard/webview/panel.js +3 -2
- package/out/analytics/identity.js +0 -0
- package/out/analytics/service.js +23 -25
- package/out/analytics/sessionEvents.js +98 -23
- package/out/auth/authBaseUrl.js +59 -0
- package/out/auth/bootstrapApply.js +9 -10
- package/out/auth/brokerClient.js +48 -14
- package/out/auth/provider.js +8 -21
- package/out/auth/sessionStore.js +3 -40
- package/out/auth/signInFlow.js +2 -1
- package/out/auth-broker/config.js +50 -5
- package/out/auth-broker/server.js +122 -26
- package/out/auth-broker/store.js +25 -24
- package/out/authority/warrant.js +206 -0
- package/out/branding.js +27 -1
- package/out/cli/commands/conformance.js +249 -0
- package/out/cli/commands/data-plane.js +67 -0
- package/out/cli/commands/deploy.js +1 -0
- package/out/cli/commands/gateway.js +4 -0
- package/out/cli/commands/pack.js +182 -3
- package/out/cli/commands/sandbox.js +332 -0
- package/out/cli/commands/serve.js +875 -52
- package/out/cli/commands/store.js +12 -4
- package/out/cli/data-plane/gateway.js +28 -5
- package/out/cli/data-plane/index.js +17 -2
- package/out/cli/data-plane/registry.js +9 -0
- package/out/cli/data-plane/remote.js +106 -0
- package/out/cli/data-plane/sidecar.js +161 -0
- package/out/cli/deploy/layers/identity.js +85 -4
- package/out/cli/deploy-init.js +89 -24
- package/out/cli/deps.js +106 -2
- package/out/cli/idp-init.js +35 -0
- package/out/cli/index.js +155 -16
- package/out/cli/pack-install-ref.js +15 -0
- package/out/cli/pack-loader.js +8 -1
- package/out/cli/pack-runtime.js +61 -0
- package/out/cli/runtime.js +35 -1
- package/out/cli/serve-hardening.js +80 -3
- package/out/cli/utils.js +12 -1
- package/out/engines/base.js +32 -3
- package/out/engines/claude.js +33 -15
- package/out/engines/codex.js +110 -26
- package/out/engines/custom.js +8 -0
- package/out/evidence/sessionShip.js +610 -0
- package/out/extension.js +11 -2
- package/out/gateway/bridge.js +40 -2
- package/out/gateway/client.js +34 -1
- package/out/gateway/installSource.js +68 -0
- package/out/gateway/lifecycle.js +185 -51
- package/out/gateway/outboundPaths.js +4 -1
- package/out/gateway/secretsPreflight.js +90 -0
- package/out/gateway/service.js +7 -2
- package/out/headless/index.js +17 -1
- package/out/live-e2e/mcp-demo/server.mjs +32 -0
- package/out/mcp.js +30 -0
- package/out/pack/caps.js +271 -0
- package/out/pack/governor.js +309 -0
- package/out/pack/lease-schema.js +270 -0
- package/out/pack/lease.js +640 -0
- package/out/pack/reconcile.js +217 -0
- package/out/pack/signature.js +839 -0
- package/out/remote/handleRemoteChat.js +44 -3
- package/out/remote/remoteTrust.js +277 -0
- package/out/remote/serveClient.js +52 -5
- package/out/runner/host.js +131 -0
- package/out/runner/protocol.js +108 -0
- package/out/runner/remoteEngine.js +130 -0
- package/out/runtime/interfaces/edit-tracker.js +2 -0
- package/out/runtime/vscode/vscode-edit-tracker.js +2 -0
- package/out/sandbox/containerDriver.js +570 -0
- package/out/sandbox/processDriver.js +315 -0
- package/out/sandbox/sandboxRunner.js +191 -0
- package/out/sandbox/support.js +301 -0
- package/out/sandbox/types.js +55 -0
- package/out/scripts/agentide-litellm.sh +67 -5
- package/out/scripts/agentide_litellm_hooks_v1.py +182 -21
- package/out/scripts/analytics/agentide-litellm-otel.config.yaml +94 -132
- package/out/scripts/analytics/setup-analytics.ps1 +37 -13
- package/out/scripts/analytics/setup-analytics.sh +93 -13
- package/out/scripts/analytics/start-analytics.sh +86 -14
- package/out/scripts/gateway/setup-gateway.sh +72 -32
- package/out/shared/models.js +11 -110
- package/out/spotlight/install.js +7 -1
- package/out/store/index.js +41 -10
- package/out/sync.js +3 -1
- package/out/test/analytics.test.js +162 -23
- package/out/test/auth-baseurl.test.js +132 -0
- package/out/test/auth-bootstrapApply.test.js +27 -6
- package/out/test/auth-broker-hardening.test.js +227 -0
- package/out/test/auth-broker.test.js +59 -9
- package/out/test/branding.test.js +13 -0
- package/out/test/caps-governor.test.js +455 -0
- package/out/test/codex-new-file-survival.test.js +119 -0
- package/out/test/configuration-scopes.test.js +131 -0
- package/out/test/data-plane.test.js +67 -0
- package/out/test/deploy-init.test.js +40 -0
- package/out/test/gateway-client.test.js +28 -0
- package/out/test/gateway-install-source.test.js +111 -0
- package/out/test/gateway-lifecycle.test.js +1 -1
- package/out/test/gateway-outbound.test.js +7 -0
- package/out/test/gateway-secrets-preflight.test.js +127 -0
- package/out/test/gateway-session-routing.test.js +18 -0
- package/out/test/governor-hardening.test.js +217 -0
- package/out/test/headless-sync.test.js +22 -0
- package/out/test/lease-signature.test.js +357 -0
- package/out/test/lease-trust-root.test.js +273 -0
- package/out/test/lease-validation.test.js +169 -0
- package/out/test/lease.test.js +114 -0
- package/out/test/mcp.test.js +27 -1
- package/out/test/pack-admission.test.js +216 -0
- package/out/test/pack-digest-parity.test.js +140 -0
- package/out/test/pack-reconcile.test.js +183 -0
- package/out/test/pack-signature-artifact.test.js +103 -0
- package/out/test/pack-signature.test.js +176 -0
- package/out/test/pack-trust-root.test.js +202 -0
- package/out/test/remote-trust.test.js +242 -0
- package/out/test/runner-protocol.test.js +249 -0
- package/out/test/sandbox-driver.test.js +553 -0
- package/out/test/sandbox-hardening.test.js +395 -0
- package/out/test/sandbox-runner.test.js +343 -0
- package/out/test/serve-hardening.test.js +45 -6
- package/out/test/serveClient.test.js +6 -5
- package/out/test/sessionShip.test.js +560 -0
- package/out/test/spotlightPublish.test.js +28 -0
- package/out/test/store-install-safe.test.js +69 -0
- package/out/test/tls-bypass.test.js +90 -0
- package/out/test/warrant.test.js +138 -0
- package/out/utils.js +46 -9
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#---------------------------------------------------------------------------------------------
|
|
2
2
|
# Copyright (c) AgentHippo.ai. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
|
|
3
4
|
#---------------------------------------------------------------------------------------------
|
|
4
5
|
"""
|
|
5
6
|
AgentIDE LiteLLM proxy hooks - Clean structured assistant output with separate input/output
|
|
@@ -7,11 +8,27 @@ Mimics langfuse_otel style: gen_ai.input.messages (history) + gen_ai.output.mess
|
|
|
7
8
|
|
|
8
9
|
Usage in litellm config.yaml:
|
|
9
10
|
callbacks: ["agentide_litellm_hooks_v1.clean_separated_otel_output"]
|
|
11
|
+
|
|
12
|
+
PRIVACY DEFAULT (PLAN/compliance-req.md §6 "data minimization"):
|
|
13
|
+
Prompt and completion CONTENT is NOT captured unless an operator opts in with
|
|
14
|
+
|
|
15
|
+
AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE=1
|
|
16
|
+
|
|
17
|
+
Without the opt-in every span carries metadata only — model, tokens, latency, status — plus a
|
|
18
|
+
sha256 + byte length of the content so a later disclosure can still be *matched* against a
|
|
19
|
+
span without the span holding the text. This is enforced here at span-write time, in addition
|
|
20
|
+
to `turn_off_message_logging: true` in the config, so a config that re-enables message logging
|
|
21
|
+
without the env opt-in still produces redacted spans (defence in depth: the config is edited by
|
|
22
|
+
hand far more often than this file).
|
|
10
23
|
"""
|
|
11
24
|
|
|
12
25
|
from __future__ import annotations
|
|
13
26
|
from typing import Any
|
|
27
|
+
import hashlib
|
|
14
28
|
import json
|
|
29
|
+
import os
|
|
30
|
+
import re
|
|
31
|
+
import sys
|
|
15
32
|
|
|
16
33
|
from litellm.integrations.custom_logger import CustomLogger
|
|
17
34
|
from litellm.integrations.opentelemetry import OpenTelemetry
|
|
@@ -20,6 +37,109 @@ from opentelemetry.trace import Status, StatusCode
|
|
|
20
37
|
_OTEL_TEXT_MAX_LEN = 4000 # per text block
|
|
21
38
|
_OTEL_PREVIEW_LEN = 180 # short preview in event
|
|
22
39
|
|
|
40
|
+
# -----------------------------------------------------------------------------
|
|
41
|
+
# Content capture opt-in (fix: full capture used to be the default)
|
|
42
|
+
# -----------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
CONTENT_CAPTURE_ENV = "AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE"
|
|
45
|
+
|
|
46
|
+
_TRUTHY = frozenset({"1", "true", "yes", "on"})
|
|
47
|
+
|
|
48
|
+
# Span attributes that can carry prompt/completion text. Mirrors CONTENT_ATTRS in
|
|
49
|
+
# @agenthippo-ai/evidence-ship so the two redaction points cannot drift silently.
|
|
50
|
+
_CONTENT_ATTR_RE = re.compile(
|
|
51
|
+
r"^(gen_ai\.(input|output)\.messages"
|
|
52
|
+
r"|gen_ai\.(prompt|completion)"
|
|
53
|
+
r"|gen_ai\.system_instructions"
|
|
54
|
+
r"|llm\.(prompts|responses)"
|
|
55
|
+
r"|llm\.request\.functions\."
|
|
56
|
+
r"|error\.(message|stack_trace)"
|
|
57
|
+
r"|hidden_params"
|
|
58
|
+
r"|metadata\.requester_custom_headers)"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
_capture_banner_printed = False
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _content_capture_enabled() -> bool:
|
|
65
|
+
"""True only when an operator explicitly opted in to prompt/completion capture."""
|
|
66
|
+
return (os.environ.get(CONTENT_CAPTURE_ENV, "") or "").strip().lower() in _TRUTHY
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _announce_content_capture() -> None:
|
|
70
|
+
"""Log loudly, once, whichever mode is in force — silence is how this got shipped on."""
|
|
71
|
+
global _capture_banner_printed
|
|
72
|
+
if _capture_banner_printed:
|
|
73
|
+
return
|
|
74
|
+
_capture_banner_printed = True
|
|
75
|
+
if _content_capture_enabled():
|
|
76
|
+
print(
|
|
77
|
+
"\n"
|
|
78
|
+
"!! AGENTHIPPO EVIDENCE: FULL PROMPT/COMPLETION CAPTURE IS ON !!\n"
|
|
79
|
+
f"!! {CONTENT_CAPTURE_ENV} is set. Every span will carry prompt and completion text.\n"
|
|
80
|
+
"!! Unset it to return to metadata-only (sha256 + length) spans.\n",
|
|
81
|
+
file=sys.stderr,
|
|
82
|
+
flush=True,
|
|
83
|
+
)
|
|
84
|
+
else:
|
|
85
|
+
print(
|
|
86
|
+
f"[agentide-otel] content capture OFF (metadata + sha256 only). "
|
|
87
|
+
f"Set {CONTENT_CAPTURE_ENV}=1 to record prompts/completions.",
|
|
88
|
+
file=sys.stderr,
|
|
89
|
+
flush=True,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _sha256(text: str) -> str:
|
|
94
|
+
return hashlib.sha256(text.encode("utf-8", "replace")).hexdigest()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class _RedactingSpan:
|
|
98
|
+
"""
|
|
99
|
+
Span proxy that drops content-bearing attributes and replaces them with a sha256 + length.
|
|
100
|
+
|
|
101
|
+
LiteLLM's own `set_attributes()` writes `gen_ai.input.messages` (the full history) and
|
|
102
|
+
friends. We cannot remove an attribute after the SDK has recorded it, so the only place to
|
|
103
|
+
stop it is between LiteLLM and the SDK — here. Everything that is not content passes through
|
|
104
|
+
untouched, so the span keeps model, tokens, latency, status, and the requester metadata the
|
|
105
|
+
evidence index reads.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
def __init__(self, span: Any) -> None:
|
|
109
|
+
self._span = span
|
|
110
|
+
|
|
111
|
+
def __getattr__(self, name: str) -> Any: # everything we do not override
|
|
112
|
+
return getattr(self._span, name)
|
|
113
|
+
|
|
114
|
+
def set_attribute(self, key: Any, value: Any) -> Any:
|
|
115
|
+
if isinstance(key, str) and _CONTENT_ATTR_RE.match(key):
|
|
116
|
+
text = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str)
|
|
117
|
+
self._span.set_attribute(f"{key}.sha256", _sha256(text))
|
|
118
|
+
self._span.set_attribute(f"{key}.length", len(text))
|
|
119
|
+
self._span.set_attribute(f"{key}.redacted", True)
|
|
120
|
+
return None
|
|
121
|
+
return self._span.set_attribute(key, value)
|
|
122
|
+
|
|
123
|
+
def set_attributes(self, attributes: Any) -> Any:
|
|
124
|
+
if isinstance(attributes, dict):
|
|
125
|
+
for key, value in attributes.items():
|
|
126
|
+
self.set_attribute(key, value)
|
|
127
|
+
return None
|
|
128
|
+
return self._span.set_attributes(attributes)
|
|
129
|
+
|
|
130
|
+
def add_event(self, name: Any, attributes: Any = None, **kwargs: Any) -> Any:
|
|
131
|
+
if isinstance(attributes, dict):
|
|
132
|
+
filtered = {}
|
|
133
|
+
for key, value in attributes.items():
|
|
134
|
+
if isinstance(key, str) and _CONTENT_ATTR_RE.match(key):
|
|
135
|
+
text = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str)
|
|
136
|
+
filtered[f"{key}.sha256"] = _sha256(text)
|
|
137
|
+
filtered[f"{key}.length"] = len(text)
|
|
138
|
+
else:
|
|
139
|
+
filtered[key] = value
|
|
140
|
+
attributes = filtered
|
|
141
|
+
return self._span.add_event(name, attributes, **kwargs)
|
|
142
|
+
|
|
23
143
|
|
|
24
144
|
def _read_field(obj: Any, key: str, default: Any = None) -> Any:
|
|
25
145
|
if isinstance(obj, dict):
|
|
@@ -103,9 +223,16 @@ def _is_anthropic_route(model: Any) -> bool:
|
|
|
103
223
|
return "claude" in name or "anthropic" in name
|
|
104
224
|
|
|
105
225
|
|
|
226
|
+
# Every model known to reject the Codex built-in web-search tools. Additive by policy: a
|
|
227
|
+
# deployment that still routes to an older model keeps its sanitizer when a newer one is added,
|
|
228
|
+
# because a dropped entry fails as upstream 400s on live traffic, not as a test failure.
|
|
229
|
+
_WEB_SEARCH_UNSUPPORTED_MODELS = ("gpt-5.6-luna", "gpt-4.1-nano")
|
|
230
|
+
|
|
231
|
+
|
|
106
232
|
def _is_web_search_unsupported_model(model: Any) -> bool:
|
|
107
233
|
"""True when the upstream model rejects Codex web-search tools."""
|
|
108
|
-
|
|
234
|
+
name = _model_name(model)
|
|
235
|
+
return any(m in name for m in _WEB_SEARCH_UNSUPPORTED_MODELS)
|
|
109
236
|
|
|
110
237
|
|
|
111
238
|
# OpenAI Responses API fields that Anthropic (and some gateways) reject if forwarded verbatim.
|
|
@@ -360,7 +487,7 @@ def sanitize_messages(messages: Any) -> Any:
|
|
|
360
487
|
|
|
361
488
|
|
|
362
489
|
class ReasoningParamsSanitizer(CustomLogger):
|
|
363
|
-
"""LiteLLM proxy hook: strip reasoning controls for gpt-
|
|
490
|
+
"""LiteLLM proxy hook: strip reasoning controls for gpt-5.6-luna."""
|
|
364
491
|
|
|
365
492
|
async def async_pre_call_hook(
|
|
366
493
|
self,
|
|
@@ -410,7 +537,7 @@ class CodexProxySanitizer(CustomLogger):
|
|
|
410
537
|
LiteLLM proxy hook: strip Codex/OpenAI Responses fields/tools that break proxy routes.
|
|
411
538
|
|
|
412
539
|
- Anthropic: drop client_metadata (OpenAI-only Responses telemetry).
|
|
413
|
-
- gpt-
|
|
540
|
+
- gpt-5.6-luna: drop web_search_preview and related built-in search tools.
|
|
414
541
|
"""
|
|
415
542
|
|
|
416
543
|
def _sanitize(self, payload: dict, model: Any) -> dict:
|
|
@@ -548,8 +675,36 @@ class CleanSeparatedOtelOutput(OpenTelemetry):
|
|
|
548
675
|
- gen_ai.input.messages → full history (as set by LiteLLM)
|
|
549
676
|
- gen_ai.output.messages → only the new assistant reply (stringified structured array)
|
|
550
677
|
- Short preview event for quick visibility
|
|
678
|
+
|
|
679
|
+
Content (both directions) is recorded ONLY when AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE is set.
|
|
680
|
+
Otherwise every content attribute becomes `<key>.sha256` + `<key>.length` — enough to prove
|
|
681
|
+
which bytes a turn saw, without shipping the bytes.
|
|
551
682
|
"""
|
|
552
683
|
|
|
684
|
+
def set_attributes(self, span: Any, *args: Any, **kwargs: Any) -> Any:
|
|
685
|
+
"""
|
|
686
|
+
Redaction chokepoint. Overriding here rather than at the call site covers every path in
|
|
687
|
+
the base class that sets attributes, including ones this subclass does not override.
|
|
688
|
+
"""
|
|
689
|
+
_announce_content_capture()
|
|
690
|
+
if not _content_capture_enabled():
|
|
691
|
+
span = _RedactingSpan(span)
|
|
692
|
+
return super().set_attributes(span, *args, **kwargs)
|
|
693
|
+
|
|
694
|
+
def _get_span_name(self, kwargs: dict) -> str:
|
|
695
|
+
# Evidence pipeline indexes spans named litellm_request; main-stable semconv uses "chat …".
|
|
696
|
+
#
|
|
697
|
+
# The constant is a PRIVATE litellm symbol and this runs on the span-creation path, so an
|
|
698
|
+
# upstream rename would raise for every span — total evidence loss at layer 1, not a
|
|
699
|
+
# degraded span name. The literal is the same value; falling back to it keeps the pipeline
|
|
700
|
+
# indexing while the import is fixed.
|
|
701
|
+
try:
|
|
702
|
+
from litellm.integrations.opentelemetry import LITELLM_REQUEST_SPAN_NAME
|
|
703
|
+
|
|
704
|
+
return LITELLM_REQUEST_SPAN_NAME
|
|
705
|
+
except Exception: # pragma: no cover - depends on the pinned litellm build
|
|
706
|
+
return "litellm_request"
|
|
707
|
+
|
|
553
708
|
def _start_primary_span(
|
|
554
709
|
self,
|
|
555
710
|
kwargs: dict,
|
|
@@ -573,24 +728,30 @@ class CleanSeparatedOtelOutput(OpenTelemetry):
|
|
|
573
728
|
_extract_assistant_text(response_obj)
|
|
574
729
|
|
|
575
730
|
if text.strip():
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
"gen_ai.
|
|
592
|
-
|
|
593
|
-
|
|
731
|
+
capture = _content_capture_enabled()
|
|
732
|
+
event_attrs: dict[str, Any] = {
|
|
733
|
+
# Always present: proves a reply of exactly these bytes was produced, and lets a
|
|
734
|
+
# disclosed transcript be matched back to the span without storing the text.
|
|
735
|
+
"gen_ai.output.sha256": _sha256(text),
|
|
736
|
+
"gen_ai.output.length": len(text),
|
|
737
|
+
"gen_ai.output.truncated": len(text) > _OTEL_TEXT_MAX_LEN,
|
|
738
|
+
"gen_ai.output.captured": capture,
|
|
739
|
+
"gen_ai.response.model": _read_field(kwargs, "model", "unknown"),
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if capture:
|
|
743
|
+
# Structured output (separate from input history) — opt-in only.
|
|
744
|
+
structured_msgs = _make_output_messages_structured(text)
|
|
745
|
+
span.set_attribute(
|
|
746
|
+
"gen_ai.output.messages",
|
|
747
|
+
json.dumps(structured_msgs, ensure_ascii=False)
|
|
748
|
+
)
|
|
749
|
+
# Short preview event - easy to spot, low noise
|
|
750
|
+
event_attrs["gen_ai.output.preview"] = (
|
|
751
|
+
text[:_OTEL_PREVIEW_LEN] + ("..." if len(text) > _OTEL_PREVIEW_LEN else "")
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
span.add_event(name="gen_ai.output.generated", attributes=event_attrs)
|
|
594
755
|
|
|
595
756
|
span.end(end_time=self._to_ns(end_time))
|
|
596
757
|
return span
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
#---------------------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) AgentHippo.ai. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
|
|
4
|
+
#---------------------------------------------------------------------------------------------
|
|
2
5
|
|
|
3
6
|
# AgentIDE LiteLLM configuration with OpenTelemetry
|
|
4
7
|
# Use this instead of agentide-litellm.config.yaml for lightweight analytics
|
|
@@ -19,6 +22,20 @@
|
|
|
19
22
|
# OTEL_SERVICE_NAME=agentide-litellm # Service name in traces
|
|
20
23
|
# DEBUG_OTEL=true # Enable OTEL debug logging
|
|
21
24
|
|
|
25
|
+
#
|
|
26
|
+
# PRIVACY DEFAULT — metadata only. See "Content capture" below.
|
|
27
|
+
# AUTHENTICATION — general_settings.master_key is REQUIRED; the proxy fails to start without it.
|
|
28
|
+
# ROUTES — consumer/subscription-credential routes are NOT in this file. See
|
|
29
|
+
# agentide-litellm-consumer-routes.local-dev-only.config.yaml.
|
|
30
|
+
#
|
|
31
|
+
|
|
32
|
+
general_settings:
|
|
33
|
+
# Without a master key LiteLLM accepts unauthenticated POST /v1/chat/completions from anything
|
|
34
|
+
# that can reach the port — on a serve host that is every colocated container, and the proxy
|
|
35
|
+
# holds provider credentials. No default is supplied on purpose: an unset LITELLM_MASTER_KEY
|
|
36
|
+
# must fail closed at startup rather than quietly open the proxy.
|
|
37
|
+
master_key: os.environ/LITELLM_MASTER_KEY
|
|
38
|
+
|
|
22
39
|
litellm_settings:
|
|
23
40
|
telemetry: false # Disables sending data to LiteLLM's remote servers
|
|
24
41
|
drop_params: true
|
|
@@ -36,139 +53,84 @@ litellm_settings:
|
|
|
36
53
|
# callbacks: ["agentide_litellm_hooks_v1.structured_genai_otel"] # , "agentide_litellm_hooks.tool_order_sanitizer"]
|
|
37
54
|
# callbacks: ["agentide_litellm_hooks_v1.structured_genai_otel", "agentide_litellm_hooks_v1.reasoning_params_sanitizer"]
|
|
38
55
|
callbacks: ["agentide_litellm_hooks_v1.structured_genai_otel", "agentide_litellm_hooks_v1.codex_proxy_sanitizer", "agentide_litellm_hooks_v1.tool_order_sanitizer", "agentide_litellm_hooks_v1.empty_thinking_sanitizer", "agentide_litellm_hooks_v1.reasoning_params_sanitizer"]
|
|
39
|
-
|
|
56
|
+
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
# Content capture — OPT-IN (PLAN/compliance-req.md §6, data minimization)
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# Default: prompts and completions are NOT written to spans. Every span still carries model,
|
|
61
|
+
# token counts, latency, status, requester metadata, and a sha256 + byte length of the content,
|
|
62
|
+
# so a turn stays attributable and a disclosed transcript can be matched back to it.
|
|
63
|
+
#
|
|
64
|
+
# This used to be `turn_off_message_logging: false` + `message_logging: true`, i.e. full prompt
|
|
65
|
+
# and completion capture on by default, unredacted for the serve lane.
|
|
66
|
+
#
|
|
67
|
+
# TO RECORD PROMPTS/COMPLETIONS (both steps are required — either alone keeps you redacted):
|
|
68
|
+
# 1. set turn_off_message_logging: false and callback_settings.otel.message_logging: true
|
|
69
|
+
# 2. export AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE=1
|
|
70
|
+
# Step 2 is enforced in agentide_litellm_hooks_v1.py at span-write time and logs loudly when on.
|
|
71
|
+
turn_off_message_logging: true
|
|
40
72
|
|
|
41
73
|
callback_settings:
|
|
42
74
|
otel:
|
|
43
|
-
message_logging:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
model_info:
|
|
65
|
-
upstream_provider: "anthropic-setup-token"
|
|
66
|
-
|
|
67
|
-
# Add this under (existing) model_list:
|
|
68
|
-
# model_list:
|
|
69
|
-
# - model_name: ...
|
|
70
|
-
- model_name: litellm/claude-haiku-4-5-20251001
|
|
71
|
-
litellm_params:
|
|
72
|
-
model: anthropic/claude-haiku-4-5-20251001
|
|
73
|
-
top_p: null
|
|
74
|
-
drop_params: true
|
|
75
|
-
api_base: https://api.anthropic.com
|
|
76
|
-
|
|
77
|
-
- model_name: litellm/claude-sonnet-4-6
|
|
78
|
-
litellm_params:
|
|
79
|
-
model: anthropic/claude-sonnet-4-6
|
|
80
|
-
api_base: https://api.anthropic.com
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# Add this under (existing) model_list:
|
|
84
|
-
# model_list:
|
|
85
|
-
# - model_name: ...
|
|
86
|
-
- model_name: litellm/gpt-5.3-codex
|
|
87
|
-
litellm_params:
|
|
88
|
-
model: openai/gpt-5.3-codex
|
|
89
|
-
store: false
|
|
90
|
-
api_key: os.environ/OPENAI_CODEX_API_KEY
|
|
91
|
-
api_base: https://chatgpt.com/backend-api/codex
|
|
92
|
-
reasoning_effort: "medium"
|
|
93
|
-
additional_drop_params: ["user", "max_output_tokens"] # Must be set at specific model -- global does not work: Force LiteLLM to remove this specific field before sending the request
|
|
94
|
-
|
|
95
|
-
- model_name: litellm/gpt-5.5
|
|
96
|
-
litellm_params:
|
|
97
|
-
model: openai/gpt-5.5
|
|
98
|
-
store: false
|
|
99
|
-
api_key: os.environ/OPENAI_API_KEY
|
|
100
|
-
api_base: https://api.openai.com/v1
|
|
101
|
-
reasoning_effort: "medium"
|
|
102
|
-
additional_drop_params: ["user", "max_output_tokens"] # Must be set at specific model -- global does not work: Force LiteLLM to remove this specific field before sending the request
|
|
103
|
-
|
|
104
|
-
# Add this under (existing) model_list:
|
|
105
|
-
# model_list:
|
|
106
|
-
# - model_name: ...
|
|
107
|
-
- model_name: litellm/gpt-5.1-codex-mini
|
|
108
|
-
litellm_params:
|
|
109
|
-
model: openai/gpt-5.1-codex-mini
|
|
110
|
-
reasoning_effort: "medium"
|
|
111
|
-
additional_drop_params: ["user", "max_output_tokens", "temperature"]
|
|
112
|
-
store: false
|
|
113
|
-
api_key: os.environ/OPENAI_CODEX_API_KEY
|
|
114
|
-
api_base: https://chatgpt.com/backend-api/codex
|
|
115
|
-
|
|
116
|
-
# Add this under (existing) model_list:
|
|
117
|
-
# model_list:
|
|
118
|
-
# - model_name: ...
|
|
119
|
-
- model_name: litellm/claude-opus-4-6-subs
|
|
120
|
-
litellm_params:
|
|
121
|
-
model: anthropic/claude-opus-4-6
|
|
122
|
-
reasoning_effort: "medium"
|
|
123
|
-
max_tokens: 64000
|
|
124
|
-
additional_drop_params: ["user", "max_output_tokens"]
|
|
125
|
-
store: false
|
|
126
|
-
api_key: os.environ/ANTHROPIC_SETUP_TOKEN
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
- model_name: litellm/claude-haiku-4-5-subs
|
|
130
|
-
litellm_params:
|
|
131
|
-
model: anthropic/claude-haiku-4-5
|
|
132
|
-
reasoning_effort: "medium"
|
|
133
|
-
additional_drop_params: ["user", "max_output_tokens"]
|
|
134
|
-
store: false
|
|
135
|
-
api_key: os.environ/ANTHROPIC_SETUP_TOKEN
|
|
136
|
-
|
|
137
|
-
# Add this under (existing) model_list:
|
|
138
|
-
# model_list:
|
|
139
|
-
# - model_name: ...
|
|
140
|
-
- model_name: litellm/claude-opus-4-7-test
|
|
141
|
-
litellm_params:
|
|
142
|
-
model: anthropic/claude-opus-4-7
|
|
143
|
-
reasoning_effort: "medium"
|
|
144
|
-
additional_drop_params: ["user", "max_output_tokens"]
|
|
145
|
-
store: false
|
|
146
|
-
model_info:
|
|
147
|
-
upstream_provider: "anthropic"
|
|
75
|
+
message_logging: false # true + AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE=1 records tool_calls, arguments, responses
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# Model routes
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
# A fresh install ships with no active routes. Uncomment `model_list:` and the
|
|
81
|
+
# provider blocks you use, then put the matching key in ~/.agent-hippo/.env
|
|
82
|
+
# (e.g. echo 'OPENAI_API_KEY=sk-...' >> ~/.agent-hippo/.env). The launcher
|
|
83
|
+
# warns at startup about any referenced env var that is not set.
|
|
84
|
+
#
|
|
85
|
+
# NOTE: `additional_drop_params` must be set per model — the global setting
|
|
86
|
+
# does not work.
|
|
87
|
+
# More providers: https://docs.litellm.ai/docs/providers
|
|
88
|
+
#
|
|
89
|
+
# Consumer/subscription-credential routes (litellm/gpt-5.3-codex,
|
|
90
|
+
# litellm/claude-opus-4-7, …) are deliberately NOT in this file — see
|
|
91
|
+
# agentide-litellm-consumer-routes.local-dev-only.config.yaml and
|
|
92
|
+
# PLAN/compliance.md §12. `litellm/gpt-5.3-codex` is the default model in
|
|
93
|
+
# several docs/manifests; it has NOT been re-pointed at a platform-API route
|
|
94
|
+
# here, because which platform model id is equivalent is a provider question,
|
|
95
|
+
# not a config one — pick one deliberately rather than inheriting a guess.
|
|
148
96
|
|
|
149
|
-
# Add this under (existing) model_list:
|
|
150
97
|
# model_list:
|
|
151
|
-
#
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
98
|
+
# # --- OpenAI (platform API) ---
|
|
99
|
+
# - model_name: litellm/gpt-5.5
|
|
100
|
+
# litellm_params:
|
|
101
|
+
# model: openai/gpt-5.5
|
|
102
|
+
# api_base: https://api.openai.com/v1
|
|
103
|
+
# api_key: os.environ/OPENAI_API_KEY
|
|
104
|
+
# drop_params: true
|
|
105
|
+
# additional_drop_params: ["user", "max_output_tokens"]
|
|
106
|
+
#
|
|
107
|
+
# # --- Anthropic (platform API) ---
|
|
108
|
+
# - model_name: litellm/claude-sonnet-4-6
|
|
109
|
+
# litellm_params:
|
|
110
|
+
# model: anthropic/claude-sonnet-4-6
|
|
111
|
+
# api_base: https://api.anthropic.com
|
|
112
|
+
# api_key: os.environ/ANTHROPIC_API_KEY
|
|
113
|
+
# drop_params: true
|
|
114
|
+
#
|
|
115
|
+
# # --- AgentHippo model gateway (fleet / Cloud Run) ---
|
|
116
|
+
# # Same endpoint as colocated agents with
|
|
117
|
+
# # AGENTHIPPO_LITELLM_BASE_URL=https://gateway.agenthippo.ai/v1.
|
|
118
|
+
# # AGENTHIPPO_GATEWAY_KEY is an ahgw_… service key (create-gateway-key on
|
|
119
|
+
# # the gateway host).
|
|
120
|
+
# - model_name: ah-auto
|
|
121
|
+
# litellm_params:
|
|
122
|
+
# # openai/ prefix required — bare `ah-auto` fails LiteLLM router
|
|
123
|
+
# # deployment (no provider).
|
|
124
|
+
# model: openai/ah-auto
|
|
125
|
+
# api_base: https://gateway.agenthippo.ai/v1
|
|
126
|
+
# api_key: os.environ/AGENTHIPPO_GATEWAY_KEY
|
|
127
|
+
# drop_params: true
|
|
128
|
+
# additional_drop_params: ["user", "max_output_tokens", "reasoning.effort", "reasoning_effort"]
|
|
129
|
+
#
|
|
130
|
+
# # --- Distributed LiteLLM OTEL: central model gateway ---
|
|
131
|
+
# - model_name: ah-auto
|
|
132
|
+
# litellm_params:
|
|
133
|
+
# model: openai/ah-auto
|
|
134
|
+
# api_base: https://gatewayv1.agenthippo.ai/v1
|
|
135
|
+
# api_key: os.environ/AGENTHIPPO_GATEWAY_KEY
|
|
136
|
+
# drop_params: true
|
|
@@ -282,6 +282,37 @@ function Resolve-LiteLLMHooks {
|
|
|
282
282
|
return $null
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
+
# Ensure the proxy has an auth key without ever asking the user for one.
|
|
286
|
+
# Mirrors ensure_litellm_master_key in setup-analytics.sh — the launcher refuses to start
|
|
287
|
+
# unauthenticated, so a first run with no key would leave LiteLLM dead with the error in a log.
|
|
288
|
+
function Ensure-LiteLLMMasterKey {
|
|
289
|
+
$envFile = Join-Path $AgentHomeDir ".env"
|
|
290
|
+
|
|
291
|
+
if ((Test-Path $envFile) -and (Select-String -Path $envFile -Pattern '^\s*LITELLM_MASTER_KEY=' -Quiet)) {
|
|
292
|
+
Log-Success "LiteLLM master key already configured"
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
if ($env:LITELLM_MASTER_KEY) {
|
|
296
|
+
Log-Success "Using LITELLM_MASTER_KEY from the environment"
|
|
297
|
+
return
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
$bytes = New-Object byte[] 24
|
|
301
|
+
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
|
|
302
|
+
$key = "sk-" + (($bytes | ForEach-Object { $_.ToString("x2") }) -join "")
|
|
303
|
+
|
|
304
|
+
New-Item -ItemType Directory -Path $AgentHomeDir -Force | Out-Null
|
|
305
|
+
$lines = @(
|
|
306
|
+
"# Generated by setup-analytics - authenticates the local LiteLLM proxy.",
|
|
307
|
+
"# Clients read the same value as AGENTHIPPO_LITELLM_API_KEY.",
|
|
308
|
+
"LITELLM_MASTER_KEY=$key",
|
|
309
|
+
"AGENTHIPPO_LITELLM_API_KEY=$key"
|
|
310
|
+
)
|
|
311
|
+
Add-Content -Path $envFile -Value $lines
|
|
312
|
+
$env:LITELLM_MASTER_KEY = $key
|
|
313
|
+
Log-Success "Generated a LiteLLM master key (stored in $envFile)"
|
|
314
|
+
}
|
|
315
|
+
|
|
285
316
|
function Setup-Config {
|
|
286
317
|
Log-Step "Setting up configuration..."
|
|
287
318
|
|
|
@@ -289,6 +320,9 @@ function Setup-Config {
|
|
|
289
320
|
New-Item -ItemType Directory -Path (Join-Path $AnalyticsDir "config") -Force | Out-Null
|
|
290
321
|
New-Item -ItemType Directory -Path (Join-Path $AnalyticsDir "traces") -Force | Out-Null
|
|
291
322
|
|
|
323
|
+
# Before any early return below - the launcher exits non-zero without a key.
|
|
324
|
+
Ensure-LiteLLMMasterKey
|
|
325
|
+
|
|
292
326
|
# Copy OTEL config
|
|
293
327
|
$configDest = Join-Path $AnalyticsDir "config\otel-collector.yaml"
|
|
294
328
|
$configSrc = Join-Path $ScriptDir "otel-collector.yaml"
|
|
@@ -330,19 +364,9 @@ function Setup-Config {
|
|
|
330
364
|
Log-Success "LiteLLM OTEL config already present (user-owned)"
|
|
331
365
|
}
|
|
332
366
|
|
|
333
|
-
#
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
$destHash = (Get-Sha256Hash $litellmDest)
|
|
337
|
-
if ($sourceHash -ne $destHash) {
|
|
338
|
-
try {
|
|
339
|
-
Copy-Item $litellmDest $litellmSrc -Force
|
|
340
|
-
Log-Success "Synced LiteLLM OTEL config back to source template"
|
|
341
|
-
} catch {
|
|
342
|
-
Log-Info "Using user-owned LiteLLM config (source template is read-only)"
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
367
|
+
# Deliberately ONE-WAY (template -> user). Copying the user's config back over the shipped
|
|
368
|
+
# template wrote their edits into the installed extension / a git-tracked file, and model
|
|
369
|
+
# entries can carry a literal upstream api_key - a credential leak into source control.
|
|
346
370
|
|
|
347
371
|
$hooksDest = Join-Path $AgentHomeDir "agentide_litellm_hooks_v1.py"
|
|
348
372
|
$hooksSrc = Resolve-LiteLLMHooks
|