trodo-python 2.19.0__tar.gz → 2.20.0__tar.gz
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.
- {trodo_python-2.19.0 → trodo_python-2.20.0}/PKG-INFO +1 -1
- {trodo_python-2.19.0 → trodo_python-2.20.0}/pyproject.toml +1 -1
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_prompts.py +13 -21
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/__init__.py +1 -1
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/prompt_manager.py +3 -1
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/prompts/compile.py +7 -20
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/prompts/types.py +8 -14
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/PKG-INFO +1 -1
- {trodo_python-2.19.0 → trodo_python-2.20.0}/README.md +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/setup.cfg +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_anon_distinct_id.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_auto_instrument_fixes.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_cross_process_session.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_datasets.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_end_run.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_error_enrichment.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_experiments.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_get_prompt_version_types.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_llm_usage_cost.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_multi_agent_shape.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_processor_methods.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_prompt_trace.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_register_otel.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_start_run.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_user_traits.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_wrap_agent_unchanged.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/api/__init__.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/api/async_client.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/api/endpoints.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/api/http_client.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/auto/__init__.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/auto/auto_event_manager.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/client.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/__init__.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/dataset_manager.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/experiment_manager.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/group_manager.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/people_manager.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/user_manager.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/__init__.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/auto_instrument.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/context.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/helpers.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/processor.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/prompt_trace.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/register.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/transport.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/wrap_agent.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/prompts/__init__.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/prompts/template.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/queue/__init__.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/queue/batch_flusher.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/queue/event_queue.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/session/__init__.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/session/server_session.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/session/session_manager.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/types.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/user_context.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/util/__init__.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/util/lru.py +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/SOURCES.txt +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/dependency_links.txt +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/requires.txt +0 -0
- {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/top_level.txt +0 -0
|
@@ -86,7 +86,7 @@ PROMPT = {
|
|
|
86
86
|
"response_format": None,
|
|
87
87
|
"variables": [
|
|
88
88
|
{"name": "persona", "type": "string", "default": "a greeter"},
|
|
89
|
-
{"name": "who", "type": "string"
|
|
89
|
+
{"name": "who", "type": "string"},
|
|
90
90
|
],
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -106,36 +106,28 @@ def test_compile_carries_model_and_tools():
|
|
|
106
106
|
assert out.response_format is None
|
|
107
107
|
|
|
108
108
|
|
|
109
|
-
def
|
|
110
|
-
# `who`
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def test_compile_treats_none_and_empty_string_as_no_value():
|
|
117
|
-
# Both render as nothing, the exact outcome `required` exists to stop.
|
|
118
|
-
with pytest.raises(CompileError, match="'who': required"):
|
|
119
|
-
compile_prompt(PROMPT, {"who": None})
|
|
120
|
-
with pytest.raises(CompileError, match="'who': required"):
|
|
121
|
-
compile_prompt(PROMPT, {"who": ""})
|
|
109
|
+
def test_compile_renders_missing_variable_empty():
|
|
110
|
+
# `who` has no value and no default. There is deliberately no way to make
|
|
111
|
+
# that a failure: the rule is caller value -> default -> empty, and it never
|
|
112
|
+
# needs a fourth case.
|
|
113
|
+
out = compile_prompt(PROMPT, {})
|
|
114
|
+
assert out.messages[1]["content"][0] == {"type": "text", "text": "Hello "}
|
|
122
115
|
|
|
123
116
|
|
|
124
|
-
def
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
optional = {**PROMPT, "variables": [
|
|
117
|
+
def test_compile_ignores_a_legacy_required_flag():
|
|
118
|
+
# Versions in the wild still carry it. It must stay inert.
|
|
119
|
+
legacy = {**PROMPT, "variables": [
|
|
128
120
|
{"name": "persona", "type": "string", "default": "a greeter"},
|
|
129
121
|
{"name": "who", "type": "string"},
|
|
130
122
|
]}
|
|
131
|
-
out = compile_prompt(
|
|
123
|
+
out = compile_prompt(legacy, {})
|
|
132
124
|
assert out.messages[1]["content"][0] == {"type": "text", "text": "Hello "}
|
|
133
125
|
|
|
134
126
|
|
|
135
|
-
def
|
|
127
|
+
def test_compile_applies_a_default_when_omitted():
|
|
136
128
|
with_default = {**PROMPT, "variables": [
|
|
137
129
|
{"name": "persona", "type": "string", "default": "a greeter"},
|
|
138
|
-
{"name": "who", "type": "string", "
|
|
130
|
+
{"name": "who", "type": "string", "default": "world"},
|
|
139
131
|
]}
|
|
140
132
|
out = compile_prompt(with_default, {})
|
|
141
133
|
assert out.messages[1]["content"][0] == {"type": "text", "text": "Hello world"}
|
|
@@ -56,10 +56,12 @@ def _to_variables(raw: Any) -> List[PromptVariable]:
|
|
|
56
56
|
out.append(v)
|
|
57
57
|
elif isinstance(v, dict) and isinstance(v.get("name"), str):
|
|
58
58
|
out.append(
|
|
59
|
+
# Named fields only — a stored version may carry keys this SDK
|
|
60
|
+
# no longer models (`required`, retired in 2.20.0), and fetching
|
|
61
|
+
# one must never raise.
|
|
59
62
|
PromptVariable(
|
|
60
63
|
name=v["name"],
|
|
61
64
|
type=v.get("type") or "string",
|
|
62
|
-
required=bool(v.get("required")),
|
|
63
65
|
default=v.get("default"),
|
|
64
66
|
description=v.get("description"),
|
|
65
67
|
)
|
|
@@ -74,12 +74,10 @@ def build_scope(
|
|
|
74
74
|
) -> Dict[str, Any]:
|
|
75
75
|
"""Build the render scope from declarations + caller values.
|
|
76
76
|
|
|
77
|
-
Resolution order: caller value -> declared default ->
|
|
78
|
-
raises -> optional-and-absent renders empty.
|
|
77
|
+
Resolution order: caller value -> declared default -> empty.
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
check, because the default is itself a value.
|
|
79
|
+
A missing value never raises. The one thing this does reject is a value for
|
|
80
|
+
a variable the prompt does not declare, which is almost always a typo.
|
|
83
81
|
"""
|
|
84
82
|
errors: List[str] = []
|
|
85
83
|
scope: Dict[str, Any] = {}
|
|
@@ -104,24 +102,13 @@ def build_scope(
|
|
|
104
102
|
has = name in values
|
|
105
103
|
value = values.get(name) if has else None
|
|
106
104
|
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
supplied = has and value is not None and value != ""
|
|
112
|
-
|
|
113
|
-
# Missing value -> the declared default, or empty.
|
|
105
|
+
# A missing value resolves, it never fails: the declared default, or
|
|
106
|
+
# empty. That IS the point of declaring a default -- there is no third
|
|
107
|
+
# case where the caller has to have supplied something, and adding one
|
|
108
|
+
# would turn an empty render into a crash for no gain.
|
|
114
109
|
if not has or value is None:
|
|
115
110
|
value = default if has_default else ([] if type_ == "messages" else "")
|
|
116
111
|
|
|
117
|
-
# A default always satisfies the requirement, so the two never both
|
|
118
|
-
# apply; the editor won't let you author both, but a hand-written
|
|
119
|
-
# declaration can.
|
|
120
|
-
required = v.get("required") if isinstance(v, dict) else getattr(v, "required", None)
|
|
121
|
-
if not supplied and required is True and not has_default:
|
|
122
|
-
errors.append(f"variable '{name}': required, but no value was provided")
|
|
123
|
-
continue
|
|
124
|
-
|
|
125
112
|
scope[name] = _coerce(value, type_, name, errors)
|
|
126
113
|
|
|
127
114
|
# Passing something the prompt doesn't declare is nearly always a rename
|
|
@@ -26,24 +26,18 @@ class PromptVariable:
|
|
|
26
26
|
"""A declared variable.
|
|
27
27
|
|
|
28
28
|
Every competitor leaves variables undeclared and untyped — discovered by
|
|
29
|
-
regex, with no defaults
|
|
30
|
-
``compile()``
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Marking a variable required means the caller must pass a value that will
|
|
38
|
-
actually render: an omitted key, ``None`` and ``""`` all fail, since all
|
|
39
|
-
three render as nothing (``0`` and ``False`` pass). A variable with
|
|
40
|
-
a ``default`` can never fail the check — the default is a value — so the two
|
|
41
|
-
are mutually exclusive in practice.
|
|
29
|
+
regex, and with no defaults. Knowing the declared set is what lets
|
|
30
|
+
``compile()`` catch a value passed for a variable that doesn't exist,
|
|
31
|
+
instead of silently rendering the wrong prompt.
|
|
32
|
+
|
|
33
|
+
A missing value never fails. It resolves to the declared ``default``, or to
|
|
34
|
+
empty — that is what declaring a default is FOR. There is deliberately no
|
|
35
|
+
``required`` flag: it would give the author a decision to get wrong and the
|
|
36
|
+
caller a crash where an empty render would do.
|
|
42
37
|
"""
|
|
43
38
|
|
|
44
39
|
name: str
|
|
45
40
|
type: str = "string"
|
|
46
|
-
required: bool = False
|
|
47
41
|
default: Any = None
|
|
48
42
|
description: Optional[str] = None
|
|
49
43
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|