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.
Files changed (64) hide show
  1. {trodo_python-2.19.0 → trodo_python-2.20.0}/PKG-INFO +1 -1
  2. {trodo_python-2.19.0 → trodo_python-2.20.0}/pyproject.toml +1 -1
  3. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_prompts.py +13 -21
  4. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/__init__.py +1 -1
  5. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/prompt_manager.py +3 -1
  6. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/prompts/compile.py +7 -20
  7. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/prompts/types.py +8 -14
  8. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/PKG-INFO +1 -1
  9. {trodo_python-2.19.0 → trodo_python-2.20.0}/README.md +0 -0
  10. {trodo_python-2.19.0 → trodo_python-2.20.0}/setup.cfg +0 -0
  11. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_anon_distinct_id.py +0 -0
  12. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_auto_instrument_fixes.py +0 -0
  13. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_cross_process_session.py +0 -0
  14. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_datasets.py +0 -0
  15. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_end_run.py +0 -0
  16. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_error_enrichment.py +0 -0
  17. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_experiments.py +0 -0
  18. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_get_prompt_version_types.py +0 -0
  19. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_llm_usage_cost.py +0 -0
  20. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_multi_agent_shape.py +0 -0
  21. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_processor_methods.py +0 -0
  22. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_prompt_trace.py +0 -0
  23. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_register_otel.py +0 -0
  24. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_start_run.py +0 -0
  25. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_user_traits.py +0 -0
  26. {trodo_python-2.19.0 → trodo_python-2.20.0}/tests/test_wrap_agent_unchanged.py +0 -0
  27. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/api/__init__.py +0 -0
  28. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/api/async_client.py +0 -0
  29. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/api/endpoints.py +0 -0
  30. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/api/http_client.py +0 -0
  31. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/auto/__init__.py +0 -0
  32. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/auto/auto_event_manager.py +0 -0
  33. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/client.py +0 -0
  34. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/__init__.py +0 -0
  35. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/dataset_manager.py +0 -0
  36. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/experiment_manager.py +0 -0
  37. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/group_manager.py +0 -0
  38. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/people_manager.py +0 -0
  39. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/managers/user_manager.py +0 -0
  40. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/__init__.py +0 -0
  41. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/auto_instrument.py +0 -0
  42. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/context.py +0 -0
  43. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/helpers.py +0 -0
  44. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/processor.py +0 -0
  45. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/prompt_trace.py +0 -0
  46. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/register.py +0 -0
  47. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/transport.py +0 -0
  48. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/otel/wrap_agent.py +0 -0
  49. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/prompts/__init__.py +0 -0
  50. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/prompts/template.py +0 -0
  51. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/queue/__init__.py +0 -0
  52. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/queue/batch_flusher.py +0 -0
  53. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/queue/event_queue.py +0 -0
  54. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/session/__init__.py +0 -0
  55. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/session/server_session.py +0 -0
  56. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/session/session_manager.py +0 -0
  57. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/types.py +0 -0
  58. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/user_context.py +0 -0
  59. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/util/__init__.py +0 -0
  60. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo/util/lru.py +0 -0
  61. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/SOURCES.txt +0 -0
  62. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/dependency_links.txt +0 -0
  63. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/requires.txt +0 -0
  64. {trodo_python-2.19.0 → trodo_python-2.20.0}/trodo_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trodo-python
3
- Version: 2.19.0
3
+ Version: 2.20.0
4
4
  Summary: Trodo Analytics SDK for Python — server-side event tracking
5
5
  License: ISC
6
6
  Keywords: analytics,tracking,trodo,server-side
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "trodo-python"
7
- version = "2.19.0"
7
+ version = "2.20.0"
8
8
  description = "Trodo Analytics SDK for Python — server-side event tracking"
9
9
  readme = "README.md"
10
10
  license = { text = "ISC" }
@@ -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", "required": True},
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 test_compile_raises_for_required_variable_with_no_value():
110
- # `who` is required with no default. Catching it here is the point: the
111
- # alternative is paying for a call to a prompt that says "Hello ".
112
- with pytest.raises(CompileError, match="'who': required"):
113
- compile_prompt(PROMPT, {})
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 test_compile_renders_missing_optional_variable_empty():
125
- # `required` defaults to False, so a declaration that doesn't ask for it
126
- # behaves exactly as before -- no stored prompt changes meaning.
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(optional, {})
123
+ out = compile_prompt(legacy, {})
132
124
  assert out.messages[1]["content"][0] == {"type": "text", "text": "Hello "}
133
125
 
134
126
 
135
- def test_compile_lets_a_default_satisfy_required():
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", "required": True, "default": "world"},
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"}
@@ -41,7 +41,7 @@ Downstream microservice (join the caller's run instead of making a new one):
41
41
 
42
42
  from __future__ import annotations
43
43
 
44
- __version__ = "2.19.0"
44
+ __version__ = "2.20.0"
45
45
 
46
46
  from typing import Any, Callable, Dict, List, Optional, Union
47
47
 
@@ -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 -> required-and-absent
78
- raises -> optional-and-absent renders empty.
77
+ Resolution order: caller value -> declared default -> empty.
79
78
 
80
- `required` is enforced HERE, in your process, before the model call -- Trodo
81
- only stores the declaration. A variable with a default can never fail this
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
- # `required` asks a narrower question than default-substitution does:
108
- # did the caller supply something that will actually render? None and ""
109
- # both render as nothing, the very outcome the flag exists to prevent,
110
- # so neither counts. (0 and False do -- they render.)
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 and no required-ness. The declaration is what lets
30
- ``compile()`` fail before the model call rather than shipping a literal
31
- ``{{typo}}`` and finding out from the bill.
32
-
33
- ``required`` is enforced by ``compile()``, which runs in YOUR process
34
- Trodo only stores the declaration. Nothing server-side rejects a call for a
35
- missing variable, and nothing rejects a version for declaring one.
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trodo-python
3
- Version: 2.19.0
3
+ Version: 2.20.0
4
4
  Summary: Trodo Analytics SDK for Python — server-side event tracking
5
5
  License: ISC
6
6
  Keywords: analytics,tracking,trodo,server-side
File without changes
File without changes