spanforge 2.0.0__py3-none-any.whl
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.
- spanforge/__init__.py +695 -0
- spanforge/_batch_exporter.py +322 -0
- spanforge/_cli.py +3081 -0
- spanforge/_hooks.py +340 -0
- spanforge/_server.py +953 -0
- spanforge/_span.py +1015 -0
- spanforge/_store.py +287 -0
- spanforge/_stream.py +654 -0
- spanforge/_trace.py +334 -0
- spanforge/_tracer.py +253 -0
- spanforge/actor.py +141 -0
- spanforge/alerts.py +464 -0
- spanforge/auto.py +181 -0
- spanforge/baseline.py +336 -0
- spanforge/config.py +460 -0
- spanforge/consent.py +227 -0
- spanforge/consumer.py +379 -0
- spanforge/core/__init__.py +5 -0
- spanforge/core/compliance_mapping.py +1060 -0
- spanforge/cost.py +597 -0
- spanforge/debug.py +514 -0
- spanforge/drift.py +488 -0
- spanforge/egress.py +63 -0
- spanforge/eval.py +575 -0
- spanforge/event.py +1052 -0
- spanforge/exceptions.py +246 -0
- spanforge/explain.py +181 -0
- spanforge/export/__init__.py +50 -0
- spanforge/export/append_only.py +342 -0
- spanforge/export/cloud.py +349 -0
- spanforge/export/datadog.py +495 -0
- spanforge/export/grafana.py +331 -0
- spanforge/export/jsonl.py +198 -0
- spanforge/export/otel_bridge.py +291 -0
- spanforge/export/otlp.py +817 -0
- spanforge/export/otlp_bridge.py +231 -0
- spanforge/export/redis_backend.py +282 -0
- spanforge/export/webhook.py +302 -0
- spanforge/exporters/__init__.py +29 -0
- spanforge/exporters/console.py +271 -0
- spanforge/exporters/jsonl.py +144 -0
- spanforge/hitl.py +297 -0
- spanforge/inspect.py +429 -0
- spanforge/integrations/__init__.py +39 -0
- spanforge/integrations/_pricing.py +277 -0
- spanforge/integrations/anthropic.py +388 -0
- spanforge/integrations/bedrock.py +306 -0
- spanforge/integrations/crewai.py +251 -0
- spanforge/integrations/gemini.py +349 -0
- spanforge/integrations/groq.py +444 -0
- spanforge/integrations/langchain.py +349 -0
- spanforge/integrations/llamaindex.py +370 -0
- spanforge/integrations/ollama.py +286 -0
- spanforge/integrations/openai.py +370 -0
- spanforge/integrations/together.py +485 -0
- spanforge/metrics.py +393 -0
- spanforge/metrics_export.py +342 -0
- spanforge/migrate.py +278 -0
- spanforge/model_registry.py +282 -0
- spanforge/models.py +407 -0
- spanforge/namespaces/__init__.py +215 -0
- spanforge/namespaces/audit.py +253 -0
- spanforge/namespaces/cache.py +209 -0
- spanforge/namespaces/chain.py +74 -0
- spanforge/namespaces/confidence.py +69 -0
- spanforge/namespaces/consent.py +85 -0
- spanforge/namespaces/cost.py +175 -0
- spanforge/namespaces/decision.py +135 -0
- spanforge/namespaces/diff.py +146 -0
- spanforge/namespaces/drift.py +79 -0
- spanforge/namespaces/eval_.py +232 -0
- spanforge/namespaces/fence.py +180 -0
- spanforge/namespaces/guard.py +104 -0
- spanforge/namespaces/hitl.py +92 -0
- spanforge/namespaces/latency.py +69 -0
- spanforge/namespaces/prompt.py +185 -0
- spanforge/namespaces/redact.py +172 -0
- spanforge/namespaces/template.py +197 -0
- spanforge/namespaces/tool_call.py +76 -0
- spanforge/namespaces/trace.py +1006 -0
- spanforge/normalizer.py +183 -0
- spanforge/presidio_backend.py +149 -0
- spanforge/processor.py +258 -0
- spanforge/prompt_registry.py +415 -0
- spanforge/py.typed +0 -0
- spanforge/redact.py +780 -0
- spanforge/sampling.py +500 -0
- spanforge/schemas/v1.0/schema.json +170 -0
- spanforge/schemas/v2.0/schema.json +536 -0
- spanforge/signing.py +1152 -0
- spanforge/stream.py +559 -0
- spanforge/testing.py +376 -0
- spanforge/trace.py +199 -0
- spanforge/types.py +696 -0
- spanforge/ulid.py +304 -0
- spanforge/validate.py +383 -0
- spanforge-2.0.0.dist-info/METADATA +1777 -0
- spanforge-2.0.0.dist-info/RECORD +101 -0
- spanforge-2.0.0.dist-info/WHEEL +4 -0
- spanforge-2.0.0.dist-info/entry_points.txt +5 -0
- spanforge-2.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"""spanforge.prompt_registry — Prompt registry with versioning and W3C-event emission.
|
|
2
|
+
|
|
3
|
+
The prompt registry provides a centralised store for prompt templates so that
|
|
4
|
+
every rendered prompt is linked to the exact version that produced it. This
|
|
5
|
+
enables:
|
|
6
|
+
|
|
7
|
+
* **Reproducibility** — re-run any historical span with the same prompt.
|
|
8
|
+
* **A/B testing** — route traffic between prompt versions and compare results.
|
|
9
|
+
* **Audit trail** — the RFC-0001 ``llm.prompt.*`` events capture template
|
|
10
|
+
load, version change, and render events.
|
|
11
|
+
|
|
12
|
+
Quick start
|
|
13
|
+
-----------
|
|
14
|
+
::
|
|
15
|
+
|
|
16
|
+
from spanforge.prompt_registry import PromptRegistry
|
|
17
|
+
|
|
18
|
+
registry = PromptRegistry()
|
|
19
|
+
registry.register(
|
|
20
|
+
name="rag_system",
|
|
21
|
+
template="You are {role}. Answer only from: {context}",
|
|
22
|
+
version="1.0.0",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
rendered = registry.render("rag_system", {"role": "expert", "context": "...docs..."})
|
|
26
|
+
print(rendered)
|
|
27
|
+
# You are expert. Answer only from: ...docs...
|
|
28
|
+
|
|
29
|
+
# Later, update the template — version change event is emitted automatically.
|
|
30
|
+
registry.register(
|
|
31
|
+
name="rag_system",
|
|
32
|
+
template="You are {role}. Use ONLY these documents: {context}",
|
|
33
|
+
version="1.1.0",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
Module-level singleton
|
|
37
|
+
----------------------
|
|
38
|
+
A module-level ``_DEFAULT_REGISTRY`` is provided. Helper functions
|
|
39
|
+
:func:`register_prompt`, :func:`get_prompt_version`, and :func:`render_prompt`
|
|
40
|
+
delegate to it for convenience::
|
|
41
|
+
|
|
42
|
+
from spanforge.prompt_registry import register_prompt, render_prompt
|
|
43
|
+
|
|
44
|
+
register_prompt("greet", "Hello, {name}!", version="1.0.0")
|
|
45
|
+
text = render_prompt("greet", {"name": "world"})
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
import logging
|
|
51
|
+
import re
|
|
52
|
+
import time
|
|
53
|
+
from dataclasses import dataclass, field
|
|
54
|
+
from typing import Any
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
"PromptRegistry",
|
|
58
|
+
"PromptVersion",
|
|
59
|
+
"get_prompt_version",
|
|
60
|
+
"register_prompt",
|
|
61
|
+
"render_prompt",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
_log = logging.getLogger("spanforge.prompt_registry")
|
|
65
|
+
|
|
66
|
+
# Simple {placeholder} pattern (not Jinja — zero runtime dependencies).
|
|
67
|
+
_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# PromptVersion dataclass
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True)
|
|
76
|
+
class PromptVersion:
|
|
77
|
+
"""An immutable snapshot of a versioned prompt template.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
name: Registry name (e.g. ``"rag_system"``).
|
|
81
|
+
template: Raw template string with ``{variable}`` placeholders.
|
|
82
|
+
version: Semantic version string (e.g. ``"1.0.0"``).
|
|
83
|
+
variables: List of placeholder names extracted from *template*.
|
|
84
|
+
created_at: Unix timestamp when this version was registered.
|
|
85
|
+
metadata: Free-form metadata dict (author, model hint, etc.).
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
name: str
|
|
89
|
+
template: str
|
|
90
|
+
version: str
|
|
91
|
+
variables: list[str] = field(default_factory=list)
|
|
92
|
+
created_at: float = field(default_factory=time.time)
|
|
93
|
+
metadata: dict[str, Any] | None = None
|
|
94
|
+
|
|
95
|
+
def render(self, variables: dict[str, Any]) -> str:
|
|
96
|
+
"""Render the template by substituting *variables*.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
variables: Dict of ``{placeholder: value}`` pairs.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The rendered string.
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
KeyError: If a required placeholder is missing from *variables*.
|
|
106
|
+
|
|
107
|
+
Example::
|
|
108
|
+
|
|
109
|
+
pv = PromptVersion("greet", "Hello, {name}!", "1.0.0", ["name"])
|
|
110
|
+
pv.render({"name": "Alice"})
|
|
111
|
+
# "Hello, Alice!"
|
|
112
|
+
"""
|
|
113
|
+
missing = [v for v in self.variables if v not in variables]
|
|
114
|
+
if missing:
|
|
115
|
+
raise KeyError(
|
|
116
|
+
f"PromptVersion '{self.name}@{self.version}' requires variables "
|
|
117
|
+
f"{missing!r} but they were not supplied."
|
|
118
|
+
)
|
|
119
|
+
return self.template.format(**variables)
|
|
120
|
+
|
|
121
|
+
def to_dict(self) -> dict[str, Any]:
|
|
122
|
+
d: dict[str, Any] = {
|
|
123
|
+
"name": self.name,
|
|
124
|
+
"template": self.template,
|
|
125
|
+
"version": self.version,
|
|
126
|
+
"variables": self.variables,
|
|
127
|
+
"created_at": self.created_at,
|
|
128
|
+
}
|
|
129
|
+
if self.metadata is not None:
|
|
130
|
+
d["metadata"] = self.metadata
|
|
131
|
+
return d
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def from_dict(cls, data: dict[str, Any]) -> "PromptVersion":
|
|
135
|
+
return cls(
|
|
136
|
+
name=data["name"],
|
|
137
|
+
template=data["template"],
|
|
138
|
+
version=data["version"],
|
|
139
|
+
variables=list(data.get("variables", [])),
|
|
140
|
+
created_at=float(data.get("created_at", time.time())),
|
|
141
|
+
metadata=data.get("metadata"),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
# PromptRegistry
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class PromptRegistry:
|
|
151
|
+
"""Thread-safe registry of versioned prompt templates.
|
|
152
|
+
|
|
153
|
+
Multiple versions of the same template name are stored independently.
|
|
154
|
+
The *latest* version (most recently registered) is used by default when
|
|
155
|
+
calling :meth:`render`.
|
|
156
|
+
|
|
157
|
+
Example::
|
|
158
|
+
|
|
159
|
+
registry = PromptRegistry()
|
|
160
|
+
registry.register("system", "You are {role}.", version="1.0.0")
|
|
161
|
+
registry.register("system", "You are a helpful {role}.", version="2.0.0")
|
|
162
|
+
|
|
163
|
+
# Uses version 2.0.0 (latest).
|
|
164
|
+
registry.render("system", {"role": "assistant"})
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def __init__(self) -> None:
|
|
168
|
+
import threading # noqa: PLC0415
|
|
169
|
+
self._lock = threading.RLock()
|
|
170
|
+
# {name: {version: PromptVersion}}
|
|
171
|
+
self._store: dict[str, dict[str, PromptVersion]] = {}
|
|
172
|
+
# {name: version_string} — last registered version = default
|
|
173
|
+
self._latest: dict[str, str] = {}
|
|
174
|
+
|
|
175
|
+
# ------------------------------------------------------------------
|
|
176
|
+
# Registration
|
|
177
|
+
# ------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
def register(
|
|
180
|
+
self,
|
|
181
|
+
name: str,
|
|
182
|
+
template: str,
|
|
183
|
+
*,
|
|
184
|
+
version: str = "1.0.0",
|
|
185
|
+
metadata: dict[str, Any] | None = None,
|
|
186
|
+
) -> PromptVersion:
|
|
187
|
+
"""Register (or update) a prompt template.
|
|
188
|
+
|
|
189
|
+
Emits:
|
|
190
|
+
* ``llm.prompt.template.loaded`` on first registration.
|
|
191
|
+
* ``llm.prompt.version.changed`` when a *name* already exists
|
|
192
|
+
(even if the version string is the same).
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
name: Unique prompt name within this registry.
|
|
196
|
+
template: Template string with ``{variable}`` placeholders.
|
|
197
|
+
version: Semantic version string.
|
|
198
|
+
metadata: Optional free-form metadata.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
The newly created :class:`PromptVersion`.
|
|
202
|
+
"""
|
|
203
|
+
variables = _PLACEHOLDER_RE.findall(template)
|
|
204
|
+
pv = PromptVersion(
|
|
205
|
+
name=name,
|
|
206
|
+
template=template,
|
|
207
|
+
version=version,
|
|
208
|
+
variables=variables,
|
|
209
|
+
metadata=metadata,
|
|
210
|
+
)
|
|
211
|
+
with self._lock:
|
|
212
|
+
existing = self._store.get(name)
|
|
213
|
+
is_new = existing is None
|
|
214
|
+
self._store.setdefault(name, {})[version] = pv
|
|
215
|
+
previous_version = self._latest.get(name)
|
|
216
|
+
self._latest[name] = version
|
|
217
|
+
|
|
218
|
+
# Emit RFC-0001 events outside the lock.
|
|
219
|
+
self._emit_register_events(pv, is_new=is_new, previous_version=previous_version)
|
|
220
|
+
return pv
|
|
221
|
+
|
|
222
|
+
# ------------------------------------------------------------------
|
|
223
|
+
# Retrieval
|
|
224
|
+
# ------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
def get(self, name: str, version: str | None = None) -> PromptVersion:
|
|
227
|
+
"""Return the :class:`PromptVersion` for *name*.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
name: Prompt name.
|
|
231
|
+
version: Explicit version string, or ``None`` for the latest.
|
|
232
|
+
|
|
233
|
+
Raises:
|
|
234
|
+
KeyError: If *name* or *version* is not found.
|
|
235
|
+
"""
|
|
236
|
+
with self._lock:
|
|
237
|
+
versions = self._store.get(name)
|
|
238
|
+
if versions is None:
|
|
239
|
+
raise KeyError(f"No prompt registered with name={name!r}")
|
|
240
|
+
if version is None:
|
|
241
|
+
version = self._latest[name]
|
|
242
|
+
pv = versions.get(version)
|
|
243
|
+
if pv is None:
|
|
244
|
+
raise KeyError(
|
|
245
|
+
f"Prompt {name!r} has no version {version!r}. "
|
|
246
|
+
f"Available: {sorted(versions)!r}"
|
|
247
|
+
)
|
|
248
|
+
return pv
|
|
249
|
+
|
|
250
|
+
def list_versions(self, name: str) -> list[str]:
|
|
251
|
+
"""Return all registered version strings for *name*, sorted ascending."""
|
|
252
|
+
with self._lock:
|
|
253
|
+
versions = self._store.get(name)
|
|
254
|
+
if versions is None:
|
|
255
|
+
raise KeyError(f"No prompt registered with name={name!r}")
|
|
256
|
+
return sorted(versions.keys())
|
|
257
|
+
|
|
258
|
+
def list_names(self) -> list[str]:
|
|
259
|
+
"""Return all registered prompt names, sorted."""
|
|
260
|
+
with self._lock:
|
|
261
|
+
return sorted(self._store.keys())
|
|
262
|
+
|
|
263
|
+
# ------------------------------------------------------------------
|
|
264
|
+
# Rendering
|
|
265
|
+
# ------------------------------------------------------------------
|
|
266
|
+
|
|
267
|
+
def render(
|
|
268
|
+
self,
|
|
269
|
+
name: str,
|
|
270
|
+
variables: dict[str, Any],
|
|
271
|
+
*,
|
|
272
|
+
version: str | None = None,
|
|
273
|
+
span_id: str | None = None,
|
|
274
|
+
trace_id: str | None = None,
|
|
275
|
+
) -> str:
|
|
276
|
+
"""Render a prompt template, emitting a ``llm.prompt.rendered`` event.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
name: Prompt name.
|
|
280
|
+
variables: Substitution variables.
|
|
281
|
+
version: Optional version string; defaults to latest.
|
|
282
|
+
span_id: Optional parent span ID for event correlation.
|
|
283
|
+
trace_id: Optional trace ID for event correlation.
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
The rendered template string.
|
|
287
|
+
|
|
288
|
+
Raises:
|
|
289
|
+
KeyError: If the prompt name or version is not found, or if a
|
|
290
|
+
required variable is missing.
|
|
291
|
+
"""
|
|
292
|
+
pv = self.get(name, version)
|
|
293
|
+
rendered = pv.render(variables)
|
|
294
|
+
self._emit_rendered_event(pv, rendered, span_id=span_id, trace_id=trace_id)
|
|
295
|
+
return rendered
|
|
296
|
+
|
|
297
|
+
# ------------------------------------------------------------------
|
|
298
|
+
# Serialisation
|
|
299
|
+
# ------------------------------------------------------------------
|
|
300
|
+
|
|
301
|
+
def export_all(self) -> list[dict[str, Any]]:
|
|
302
|
+
"""Return a list of ``to_dict()`` dicts for all registered prompt versions."""
|
|
303
|
+
with self._lock:
|
|
304
|
+
result = []
|
|
305
|
+
for versions in self._store.values():
|
|
306
|
+
for pv in versions.values():
|
|
307
|
+
result.append(pv.to_dict())
|
|
308
|
+
return result
|
|
309
|
+
|
|
310
|
+
def import_all(self, records: list[dict[str, Any]]) -> None:
|
|
311
|
+
"""Bulk-import prompt versions from a list of dicts (no events emitted)."""
|
|
312
|
+
with self._lock:
|
|
313
|
+
for rec in records:
|
|
314
|
+
pv = PromptVersion.from_dict(rec)
|
|
315
|
+
self._store.setdefault(pv.name, {})[pv.version] = pv
|
|
316
|
+
self._latest[pv.name] = pv.version
|
|
317
|
+
|
|
318
|
+
# ------------------------------------------------------------------
|
|
319
|
+
# Internal event helpers
|
|
320
|
+
# ------------------------------------------------------------------
|
|
321
|
+
|
|
322
|
+
def _emit_register_events(
|
|
323
|
+
self,
|
|
324
|
+
pv: PromptVersion,
|
|
325
|
+
*,
|
|
326
|
+
is_new: bool,
|
|
327
|
+
previous_version: str | None,
|
|
328
|
+
) -> None:
|
|
329
|
+
try:
|
|
330
|
+
from spanforge._stream import emit_rfc_event # noqa: PLC0415
|
|
331
|
+
from spanforge.types import EventType # noqa: PLC0415
|
|
332
|
+
if is_new:
|
|
333
|
+
emit_rfc_event(
|
|
334
|
+
EventType.PROMPT_TEMPLATE_LOADED,
|
|
335
|
+
payload=pv.to_dict(),
|
|
336
|
+
)
|
|
337
|
+
else:
|
|
338
|
+
emit_rfc_event(
|
|
339
|
+
EventType.PROMPT_VERSION_CHANGED,
|
|
340
|
+
payload={
|
|
341
|
+
**pv.to_dict(),
|
|
342
|
+
"previous_version": previous_version,
|
|
343
|
+
},
|
|
344
|
+
)
|
|
345
|
+
except Exception as exc: # NOSONAR
|
|
346
|
+
_log.debug("prompt_registry: failed to emit register event: %s", exc)
|
|
347
|
+
|
|
348
|
+
def _emit_rendered_event(
|
|
349
|
+
self,
|
|
350
|
+
pv: PromptVersion,
|
|
351
|
+
rendered: str,
|
|
352
|
+
*,
|
|
353
|
+
span_id: str | None,
|
|
354
|
+
trace_id: str | None,
|
|
355
|
+
) -> None:
|
|
356
|
+
try:
|
|
357
|
+
from spanforge._stream import emit_rfc_event # noqa: PLC0415
|
|
358
|
+
from spanforge.types import EventType # noqa: PLC0415
|
|
359
|
+
emit_rfc_event(
|
|
360
|
+
EventType.PROMPT_RENDERED,
|
|
361
|
+
payload={
|
|
362
|
+
"name": pv.name,
|
|
363
|
+
"version": pv.version,
|
|
364
|
+
# Omit the rendered text to avoid leaking PII; include
|
|
365
|
+
# only the prompt name/version for correlation.
|
|
366
|
+
"rendered_length": len(rendered),
|
|
367
|
+
},
|
|
368
|
+
span_id=span_id,
|
|
369
|
+
trace_id=trace_id,
|
|
370
|
+
)
|
|
371
|
+
except Exception as exc: # NOSONAR
|
|
372
|
+
_log.debug("prompt_registry: failed to emit rendered event: %s", exc)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# ---------------------------------------------------------------------------
|
|
376
|
+
# Module-level singleton + helpers
|
|
377
|
+
# ---------------------------------------------------------------------------
|
|
378
|
+
|
|
379
|
+
_DEFAULT_REGISTRY = PromptRegistry()
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def register_prompt(
|
|
383
|
+
name: str,
|
|
384
|
+
template: str,
|
|
385
|
+
*,
|
|
386
|
+
version: str = "1.0.0",
|
|
387
|
+
metadata: dict[str, Any] | None = None,
|
|
388
|
+
) -> PromptVersion:
|
|
389
|
+
"""Register a prompt in the module-level default registry.
|
|
390
|
+
|
|
391
|
+
Convenience wrapper around :meth:`PromptRegistry.register`.
|
|
392
|
+
"""
|
|
393
|
+
return _DEFAULT_REGISTRY.register(name, template, version=version, metadata=metadata)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def get_prompt_version(name: str, version: str | None = None) -> PromptVersion:
|
|
397
|
+
"""Get a :class:`PromptVersion` from the module-level default registry."""
|
|
398
|
+
return _DEFAULT_REGISTRY.get(name, version)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def render_prompt(
|
|
402
|
+
name: str,
|
|
403
|
+
variables: dict[str, Any],
|
|
404
|
+
*,
|
|
405
|
+
version: str | None = None,
|
|
406
|
+
span_id: str | None = None,
|
|
407
|
+
trace_id: str | None = None,
|
|
408
|
+
) -> str:
|
|
409
|
+
"""Render a prompt from the module-level default registry.
|
|
410
|
+
|
|
411
|
+
Convenience wrapper around :meth:`PromptRegistry.render`.
|
|
412
|
+
"""
|
|
413
|
+
return _DEFAULT_REGISTRY.render(
|
|
414
|
+
name, variables, version=version, span_id=span_id, trace_id=trace_id
|
|
415
|
+
)
|
spanforge/py.typed
ADDED
|
File without changes
|