veris-ai 1.11.1__tar.gz → 1.12.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.

Potentially problematic release.


This version of veris-ai might be problematic. Click here for more details.

Files changed (42) hide show
  1. {veris_ai-1.11.1 → veris_ai-1.12.0}/PKG-INFO +1 -1
  2. {veris_ai-1.11.1 → veris_ai-1.12.0}/pyproject.toml +1 -1
  3. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/tool_mock.py +133 -11
  4. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/conftest.py +6 -2
  5. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/test_agents_wrapper_simple.py +4 -2
  6. veris_ai-1.12.0/tests/test_helpers.py +23 -0
  7. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/test_mcp_protocol_server_mocked.py +6 -3
  8. veris_ai-1.12.0/tests/test_token_decoding.py +132 -0
  9. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/test_tool_mock.py +45 -19
  10. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/test_veris_runner_tool_options.py +4 -2
  11. {veris_ai-1.11.1 → veris_ai-1.12.0}/uv.lock +10 -10
  12. {veris_ai-1.11.1 → veris_ai-1.12.0}/.cursor/rules/documentation-management.mdc +0 -0
  13. {veris_ai-1.11.1 → veris_ai-1.12.0}/.github/workflows/release.yml +0 -0
  14. {veris_ai-1.11.1 → veris_ai-1.12.0}/.github/workflows/test.yml +0 -0
  15. {veris_ai-1.11.1 → veris_ai-1.12.0}/.gitignore +0 -0
  16. {veris_ai-1.11.1 → veris_ai-1.12.0}/.pre-commit-config.yaml +0 -0
  17. {veris_ai-1.11.1 → veris_ai-1.12.0}/CHANGELOG.md +0 -0
  18. {veris_ai-1.11.1 → veris_ai-1.12.0}/CLAUDE.md +0 -0
  19. {veris_ai-1.11.1 → veris_ai-1.12.0}/LICENSE +0 -0
  20. {veris_ai-1.11.1 → veris_ai-1.12.0}/README.md +0 -0
  21. {veris_ai-1.11.1 → veris_ai-1.12.0}/examples/README.md +0 -0
  22. {veris_ai-1.11.1 → veris_ai-1.12.0}/examples/__init__.py +0 -0
  23. {veris_ai-1.11.1 → veris_ai-1.12.0}/examples/import_options.py +0 -0
  24. {veris_ai-1.11.1 → veris_ai-1.12.0}/examples/openai_agents_example.py +0 -0
  25. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/README.md +0 -0
  26. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/__init__.py +0 -0
  27. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/agents_wrapper.py +0 -0
  28. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/api_client.py +0 -0
  29. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/jaeger_interface/README.md +0 -0
  30. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/jaeger_interface/__init__.py +0 -0
  31. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/jaeger_interface/client.py +0 -0
  32. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/jaeger_interface/models.py +0 -0
  33. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/models.py +0 -0
  34. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/observability.py +0 -0
  35. {veris_ai-1.11.1 → veris_ai-1.12.0}/src/veris_ai/utils.py +0 -0
  36. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/README.md +0 -0
  37. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/__init__.py +0 -0
  38. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/fixtures/__init__.py +0 -0
  39. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/fixtures/http_server.py +0 -0
  40. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/fixtures/simple_app.py +0 -0
  41. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/test_agents_wrapper_extract.py +0 -0
  42. {veris_ai-1.11.1 → veris_ai-1.12.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: veris-ai
3
- Version: 1.11.1
3
+ Version: 1.12.0
4
4
  Summary: A Python package for Veris AI tools
5
5
  Project-URL: Homepage, https://github.com/veris-ai/veris-python-sdk
6
6
  Project-URL: Bug Tracker, https://github.com/veris-ai/veris-python-sdk/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "veris-ai"
7
- version = "1.11.1"
7
+ version = "1.12.0"
8
8
  description = "A Python package for Veris AI tools"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,3 +1,4 @@
1
+ import base64
1
2
  import inspect
2
3
  import json
3
4
  import logging
@@ -22,8 +23,9 @@ logger = logging.getLogger(__name__)
22
23
 
23
24
  T = TypeVar("T")
24
25
 
25
- # Context variable to store session_id for each call
26
+ # Context variables to store session_id and thread_id for each call
26
27
  _session_id_context: ContextVar[str | None] = ContextVar("veris_session_id", default=None)
28
+ _thread_id_context: ContextVar[str | None] = ContextVar("veris_thread_id", default=None)
27
29
 
28
30
 
29
31
  class VerisSDK:
@@ -38,15 +40,111 @@ class VerisSDK:
38
40
  """Get the session_id from context variable."""
39
41
  return _session_id_context.get()
40
42
 
41
- def set_session_id(self, session_id: str) -> None:
42
- """Set the session_id in context variable."""
43
+ @property
44
+ def thread_id(self) -> str | None:
45
+ """Get the thread_id from context variable."""
46
+ return _thread_id_context.get()
47
+
48
+ def _set_session_id(self, session_id: str) -> None:
49
+ """Set the session_id in context variable (private method)."""
43
50
  _session_id_context.set(session_id)
44
- logger.info(f"Session ID set to {session_id}")
45
51
 
46
- def clear_session_id(self) -> None:
47
- """Clear the session_id from context variable."""
52
+ def _set_thread_id(self, thread_id: str) -> None:
53
+ """Set the thread_id in context variable (private method)."""
54
+ _thread_id_context.set(thread_id)
55
+
56
+ def parse_token(self, token: str) -> None:
57
+ """Parse and set session_id and thread_id from a base64-encoded token.
58
+
59
+ The token must be a base64-encoded JSON object containing:
60
+ {"session_id": "...", "thread_id": "..."}
61
+
62
+ Both session_id and thread_id are required fields.
63
+
64
+ Args:
65
+ token: Base64-encoded JSON token
66
+
67
+ Raises:
68
+ ValueError: If token is not valid or missing required fields
69
+ """
70
+ try:
71
+ # Decode base64 JSON token
72
+ decoded = base64.b64decode(token.encode("utf-8")).decode("utf-8")
73
+ token_data = json.loads(decoded)
74
+
75
+ if not isinstance(token_data, dict):
76
+ raise ValueError("Token must decode to a JSON object")
77
+
78
+ if "session_id" not in token_data:
79
+ raise ValueError("Token must contain 'session_id' field")
80
+
81
+ if "thread_id" not in token_data:
82
+ raise ValueError("Token must contain 'thread_id' field")
83
+
84
+ self._set_session_id(token_data["session_id"])
85
+ self._set_thread_id(token_data["thread_id"])
86
+ logger.info(
87
+ f"Session ID set to {token_data['session_id']}, "
88
+ f"Thread ID set to {token_data['thread_id']} - mocking enabled"
89
+ )
90
+
91
+ except (ValueError, json.JSONDecodeError) as e:
92
+ error_msg = f"Invalid token format: {e}"
93
+ raise ValueError(error_msg) from e
94
+
95
+ def set_session_id(self, token: str) -> None:
96
+ """DEPRECATED: Use parse_token() instead.
97
+
98
+ Set the session_id and thread_id from a base64-encoded token.
99
+
100
+ Args:
101
+ token: Base64-encoded JSON token
102
+ """
103
+ logger.warning(
104
+ "set_session_id() is deprecated. Use parse_token() instead. "
105
+ "This method will be removed in a future version."
106
+ )
107
+ # For backwards compatibility, allow tokens without thread_id
108
+ try:
109
+ decoded = base64.b64decode(token.encode("utf-8")).decode("utf-8")
110
+ token_data = json.loads(decoded)
111
+
112
+ if not isinstance(token_data, dict):
113
+ raise ValueError("Token must decode to a JSON object")
114
+
115
+ if "session_id" not in token_data:
116
+ raise ValueError("Token must contain 'session_id' field")
117
+
118
+ self._set_session_id(token_data["session_id"])
119
+ if "thread_id" in token_data:
120
+ self._set_thread_id(token_data["thread_id"])
121
+ logger.info(f"Session ID set to {token_data['session_id']}")
122
+
123
+ except (ValueError, json.JSONDecodeError) as e:
124
+ error_msg = f"Invalid token format: {e}"
125
+ raise ValueError(error_msg) from e
126
+
127
+ def _clear_session_id(self) -> None:
128
+ """Clear the session_id from context variable (private method)."""
48
129
  _session_id_context.set(None)
49
- logger.info("Session ID cleared")
130
+
131
+ def _clear_thread_id(self) -> None:
132
+ """Clear the thread_id from context variable (private method)."""
133
+ _thread_id_context.set(None)
134
+
135
+ def clear_context(self) -> None:
136
+ """Clear the session_id and thread_id from context variables."""
137
+ self._clear_session_id()
138
+ self._clear_thread_id()
139
+ logger.info("Session ID and Thread ID cleared - mocking disabled")
140
+
141
+ def clear_session_id(self) -> None:
142
+ """DEPRECATED: Use clear_context() instead."""
143
+ logger.warning(
144
+ "clear_session_id() is deprecated. Use clear_context() instead. "
145
+ "This method will be removed in a future version."
146
+ )
147
+ self.clear_context()
50
148
 
51
149
  @property
52
150
  def fastapi_mcp(self) -> Any | None: # noqa: ANN401
@@ -69,7 +167,7 @@ class VerisSDK:
69
167
  token: str | None = Depends(oauth2_scheme),
70
168
  ) -> None:
71
169
  if token:
72
- self.set_session_id(token)
170
+ self.parse_token(token)
73
171
 
74
172
  # Create auth config with dependencies
75
173
  auth_config = AuthConfig(
@@ -176,11 +274,13 @@ class VerisSDK:
176
274
  )
177
275
  return await func(*args, **kwargs)
178
276
  parameters = get_function_parameters(func, args, kwargs)
277
+ thread_id = _thread_id_context.get()
179
278
  return mock_tool_call(
180
279
  func,
181
280
  session_id,
182
281
  parameters,
183
282
  options,
283
+ thread_id,
184
284
  )
185
285
 
186
286
  @wraps(func)
@@ -196,11 +296,13 @@ class VerisSDK:
196
296
  )
197
297
  return func(*args, **kwargs)
198
298
  parameters = get_function_parameters(func, args, kwargs)
299
+ thread_id = _thread_id_context.get()
199
300
  return mock_tool_call(
200
301
  func,
201
302
  session_id,
202
303
  parameters,
203
304
  options,
305
+ thread_id,
204
306
  )
205
307
 
206
308
  # Return the appropriate wrapper based on whether the function is async
@@ -251,11 +353,29 @@ class VerisSDK:
251
353
  )
252
354
  def mock_tool_call(
253
355
  func: Callable,
254
- session_id: str,
356
+ session_id: str, # noqa: ARG001
255
357
  parameters: dict[str, dict[str, str]],
256
358
  options: ToolCallOptions | None = None,
359
+ thread_id: str | None = None,
257
360
  ) -> object:
258
- """Mock tool call."""
361
+ """Mock tool call.
362
+
363
+ Args:
364
+ func: Function being mocked
365
+ session_id: Session ID (kept for backwards compatibility, not used)
366
+ parameters: Function parameters
367
+ options: Tool call options
368
+ thread_id: Thread ID to use as session_id in API request (required)
369
+
370
+ Raises:
371
+ ValueError: If thread_id is not provided
372
+ """
373
+ if thread_id is None:
374
+ raise ValueError(
375
+ "thread_id is required for mocking. "
376
+ "Use parse_token() to set both session_id and thread_id."
377
+ )
378
+
259
379
  options = options or ToolCallOptions()
260
380
  api_client = get_api_client()
261
381
  endpoint = api_client.tool_mock_endpoint
@@ -269,6 +389,8 @@ def mock_tool_call(
269
389
  # Get function docstring
270
390
  docstring = inspect.getdoc(func) or ""
271
391
 
392
+ # Use thread_id as session_id in the payload
393
+ payload_session_id = thread_id
272
394
  # Clean up parameters for V3 - just send values, not the nested dict
273
395
  clean_params: dict[str, Any] = {}
274
396
  for key, value in parameters.items():
@@ -281,7 +403,7 @@ def mock_tool_call(
281
403
 
282
404
  # Determine response expectation
283
405
  payload = {
284
- "session_id": session_id,
406
+ "session_id": payload_session_id,
285
407
  "response_expectation": options.response_expectation.value,
286
408
  "cache_response": bool(options.cache_response),
287
409
  "tool_call": {
@@ -27,10 +27,14 @@ def mock_context():
27
27
 
28
28
  @pytest.fixture
29
29
  def simulation_env():
30
+ import base64
31
+ import json
30
32
  from veris_ai import veris
31
33
 
32
- # Set session_id to enable simulation mode
33
- veris.set_session_id("test-session-123")
34
+ # Set session_id to enable simulation mode using proper token format
35
+ token_data = {"session_id": "test-session-123", "thread_id": "test-thread-123"}
36
+ token = base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
37
+ veris.set_session_id(token)
34
38
 
35
39
  with patch.dict(
36
40
  os.environ,
@@ -17,6 +17,7 @@ if sys.version_info >= (3, 13):
17
17
  from agents import Agent, FunctionTool
18
18
 
19
19
  from veris_ai import veris, Runner
20
+ from tests.test_helpers import create_test_token
20
21
 
21
22
  # Skip tests if no API key is available
22
23
  if not os.environ.get("OPENAI_API_KEY"):
@@ -110,8 +111,9 @@ def simulation_env_with_session():
110
111
  # Set up simulation environment
111
112
  os.environ["VERIS_ENDPOINT_URL"] = "http://localhost:8000"
112
113
 
113
- # Set session ID
114
- veris.set_session_id("test-session-123")
114
+ # Set session ID using proper token format
115
+ token = create_test_token("test-session-123")
116
+ veris.set_session_id(token)
115
117
 
116
118
  yield
117
119
 
@@ -0,0 +1,23 @@
1
+ """Helper functions for tests."""
2
+
3
+ import base64
4
+ import json
5
+
6
+
7
+ def create_test_token(
8
+ session_id: str = "test-session", thread_id: str | None = "test-thread"
9
+ ) -> str:
10
+ """Create a base64-encoded test token.
11
+
12
+ Args:
13
+ session_id: Session ID to include in token
14
+ thread_id: Thread ID to include in token (optional)
15
+
16
+ Returns:
17
+ Base64-encoded JSON token
18
+ """
19
+ token_data = {"session_id": session_id}
20
+ if thread_id is not None:
21
+ token_data["thread_id"] = thread_id
22
+
23
+ return base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
@@ -14,6 +14,7 @@ from mcp.client.streamable_http import streamablehttp_client
14
14
  from mcp.types import CallToolResult
15
15
 
16
16
  from veris_ai import veris
17
+ from tests.test_helpers import create_test_token
17
18
 
18
19
  from .fixtures.simple_app import make_simple_fastapi_app
19
20
 
@@ -145,11 +146,13 @@ def server_mocked(server_port_mocked: int, simulation_env: None) -> Generator[No
145
146
  async def test_http_tool_call_mocked(server_mocked: None, server_url_mocked: str) -> None:
146
147
  """Test HTTP tool call with mocked HTTP endpoint."""
147
148
  session_id = "test-session-id"
149
+ # Create a proper base64-encoded token
150
+ token = create_test_token(session_id, "test-thread-id")
148
151
 
149
152
  async with (
150
153
  streamablehttp_client(
151
154
  server_url_mocked + "/mcp",
152
- headers={"Authorization": f"Bearer {session_id}"},
155
+ headers={"Authorization": f"Bearer {token}"},
153
156
  ) as (read_stream, write_stream, _),
154
157
  ClientSession(read_stream, write_stream) as session,
155
158
  ):
@@ -175,8 +178,8 @@ async def test_http_tool_call_mocked(server_mocked: None, server_url_mocked: str
175
178
  payload_str = payloads[0].strip()
176
179
  payload = json.loads(payload_str)
177
180
 
178
- # Verify the session_id was passed correctly
179
- assert payload["session_id"] == session_id
181
+ # Verify the session_id was passed correctly (should use thread_id as value)
182
+ assert payload["session_id"] == "test-thread-id"
180
183
  assert payload["tool_call"]["function_name"] == "get_item"
181
184
 
182
185
  # Handle both direct value and nested value structure
@@ -0,0 +1,132 @@
1
+ """Tests for token decoding functionality."""
2
+
3
+ import base64
4
+ import json
5
+
6
+ import pytest
7
+
8
+ from veris_ai import veris
9
+
10
+
11
+ class TestTokenDecoding:
12
+ """Test token decoding and session/thread ID extraction."""
13
+
14
+ def test_decode_valid_token_with_both_ids(self):
15
+ """Test decoding a valid token with both session_id and thread_id."""
16
+ session_id = "test-session-123"
17
+ thread_id = "thread-456"
18
+ token_data = {"session_id": session_id, "thread_id": thread_id}
19
+ token = base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
20
+
21
+ veris.set_session_id(token)
22
+
23
+ assert veris.session_id == session_id
24
+ assert veris.thread_id == thread_id
25
+
26
+ veris.clear_session_id()
27
+
28
+ def test_decode_valid_token_with_only_session_id(self):
29
+ """Test decoding a token with only session_id (no thread_id)."""
30
+ session_id = "test-session-789"
31
+ token_data = {"session_id": session_id}
32
+ token = base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
33
+
34
+ veris.set_session_id(token)
35
+
36
+ assert veris.session_id == session_id
37
+ assert veris.thread_id is None
38
+
39
+ veris.clear_session_id()
40
+
41
+ def test_decode_token_with_uuid_format(self):
42
+ """Test decoding token with UUID-formatted IDs."""
43
+ session_id = "550e8400-e29b-41d4-a716-446655440000"
44
+ thread_id = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
45
+ token_data = {"session_id": session_id, "thread_id": thread_id}
46
+ token = base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
47
+
48
+ veris.set_session_id(token)
49
+
50
+ assert veris.session_id == session_id
51
+ assert veris.thread_id == thread_id
52
+
53
+ veris.clear_session_id()
54
+
55
+ def test_decode_invalid_base64_raises_error(self):
56
+ """Test that invalid base64 raises ValueError."""
57
+ invalid_token = "not-valid-base64!!!"
58
+
59
+ with pytest.raises(ValueError, match="Invalid token format"):
60
+ veris.set_session_id(invalid_token)
61
+
62
+ def test_decode_invalid_json_raises_error(self):
63
+ """Test that valid base64 but invalid JSON raises ValueError."""
64
+ invalid_json = base64.b64encode(b"not valid json").decode("utf-8")
65
+
66
+ with pytest.raises(ValueError, match="Invalid token format"):
67
+ veris.set_session_id(invalid_json)
68
+
69
+ def test_decode_non_object_json_raises_error(self):
70
+ """Test that JSON array instead of object raises ValueError."""
71
+ json_array = base64.b64encode(
72
+ json.dumps(["array", "not", "object"]).encode("utf-8")
73
+ ).decode("utf-8")
74
+
75
+ with pytest.raises(ValueError, match="Token must decode to a JSON object"):
76
+ veris.set_session_id(json_array)
77
+
78
+ def test_decode_missing_session_id_raises_error(self):
79
+ """Test that token without session_id raises ValueError."""
80
+ token_data = {"thread_id": "thread-only"}
81
+ token = base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
82
+
83
+ with pytest.raises(ValueError, match="Token must contain 'session_id' field"):
84
+ veris.set_session_id(token)
85
+
86
+ def test_clear_session_id_clears_both_values(self):
87
+ """Test that clear_session_id clears both session_id and thread_id."""
88
+ session_id = "test-session"
89
+ thread_id = "test-thread"
90
+ token_data = {"session_id": session_id, "thread_id": thread_id}
91
+ token = base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
92
+
93
+ veris.set_session_id(token)
94
+ assert veris.session_id == session_id
95
+ assert veris.thread_id == thread_id
96
+
97
+ veris.clear_session_id()
98
+ assert veris.session_id is None
99
+ assert veris.thread_id is None
100
+
101
+ def test_decode_token_with_additional_fields(self):
102
+ """Test that additional fields in token are ignored."""
103
+ session_id = "test-session"
104
+ thread_id = "test-thread"
105
+ token_data = {
106
+ "session_id": session_id,
107
+ "thread_id": thread_id,
108
+ "extra_field": "ignored",
109
+ "another": 123,
110
+ }
111
+ token = base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
112
+
113
+ veris.set_session_id(token)
114
+
115
+ assert veris.session_id == session_id
116
+ assert veris.thread_id == thread_id
117
+
118
+ veris.clear_session_id()
119
+
120
+ def test_decode_token_with_empty_strings(self):
121
+ """Test decoding token with empty string values."""
122
+ session_id = ""
123
+ thread_id = ""
124
+ token_data = {"session_id": session_id, "thread_id": thread_id}
125
+ token = base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
126
+
127
+ veris.set_session_id(token)
128
+
129
+ assert veris.session_id == session_id
130
+ assert veris.thread_id == thread_id
131
+
132
+ veris.clear_session_id()
@@ -1,4 +1,6 @@
1
1
  import asyncio
2
+ import base64
3
+ import json
2
4
  import os
3
5
  from typing import Any
4
6
  from unittest.mock import AsyncMock, Mock, patch
@@ -11,6 +13,16 @@ from veris_ai.api_client import get_api_client
11
13
  from veris_ai.tool_mock import _session_id_context
12
14
 
13
15
 
16
+ def create_test_token(
17
+ session_id: str = "test-session", thread_id: str | None = "test-thread"
18
+ ) -> str:
19
+ """Create a base64-encoded test token."""
20
+ token_data = {"session_id": session_id}
21
+ if thread_id is not None:
22
+ token_data["thread_id"] = thread_id
23
+ return base64.b64encode(json.dumps(token_data).encode("utf-8")).decode("utf-8")
24
+
25
+
14
26
  @pytest.fixture
15
27
  def tool_mock():
16
28
  return veris
@@ -50,12 +62,15 @@ async def test_mock_with_context(simulation_env):
50
62
 
51
63
  with patch.object(get_api_client(), "post") as mock_request:
52
64
  mock_request.return_value = mock_response
53
- veris.set_session_id("test-session-id")
65
+ token = create_test_token("test-session-id", "test-thread-id")
66
+ veris.parse_token(token)
54
67
  result = await test_func()
55
- # Check that the mock was called with correct session_id
68
+ # Check that the mock was called with thread_id as session_id value
56
69
  mock_request.assert_called_once()
57
70
  call_args = mock_request.call_args
58
- assert call_args[0][1]["session_id"] == "test-session-id" # payload is second arg
71
+ assert (
72
+ call_args[0][1]["session_id"] == "test-thread-id"
73
+ ) # thread_id used as session_id value
59
74
  assert result == {"result": {"mocked": True}}
60
75
 
61
76
 
@@ -68,7 +83,7 @@ async def test_mock_without_context():
68
83
  return {"result": "real"}
69
84
 
70
85
  # Clear session_id to ensure production mode
71
- veris.clear_session_id()
86
+ veris.clear_context()
72
87
 
73
88
  # In production mode, the original function should be called
74
89
  result = await test_func()
@@ -100,7 +115,8 @@ async def test_mock_missing_endpoint():
100
115
  return {"result": "real"}
101
116
 
102
117
  # Set session_id to enable simulation mode
103
- veris.set_session_id("test-session")
118
+ token = create_test_token("test-session")
119
+ veris.parse_token(token)
104
120
 
105
121
  # Invalid domain will cause ConnectError
106
122
  with pytest.raises(httpx.ConnectError):
@@ -109,6 +125,7 @@ async def test_mock_missing_endpoint():
109
125
 
110
126
  @pytest.mark.asyncio
111
127
  async def test_mock_invalid_endpoint(simulation_env):
128
+ """Test that invalid endpoint URL raises ConnectError."""
112
129
  # Use invalid URL that will trigger ConnectError
113
130
  with (
114
131
  patch.dict(os.environ, {"VERIS_API_URL": "http://invalid-nonexistent-domain-67890.local"}),
@@ -141,7 +158,8 @@ async def test_context_var_isolation():
141
158
  """Test that session_id context vars are isolated between concurrent calls."""
142
159
 
143
160
  async def set_and_check_session(session_id: str):
144
- veris.set_session_id(session_id)
161
+ token = create_test_token(session_id)
162
+ veris.parse_token(token)
145
163
  # Verify the session_id is set correctly
146
164
  assert veris.session_id == session_id
147
165
  # Simulate some async work
@@ -181,7 +199,8 @@ async def test_context_var_persistence_in_call(simulation_env):
181
199
 
182
200
  with patch.object(get_api_client(), "post", side_effect=mock_request_func) as mock_request:
183
201
  # Set session_id and call function
184
- veris.set_session_id("test-session-123")
202
+ token = create_test_token("test-session-123", "test-thread-123")
203
+ veris.parse_token(token)
185
204
  await test_func()
186
205
 
187
206
  # Verify session_id was captured
@@ -189,21 +208,22 @@ async def test_context_var_persistence_in_call(simulation_env):
189
208
  assert len(captured_session_ids) >= 1
190
209
  assert captured_session_ids[0] == "test-session-123"
191
210
 
192
- # Verify the HTTP client was called with correct session_id
211
+ # Verify the HTTP client was called with correct session_id (should use thread_id)
193
212
  mock_request.assert_called_once()
194
213
  call_args = mock_request.call_args
195
- assert call_args[0][1]["session_id"] == "test-session-123"
214
+ assert call_args[0][1]["session_id"] == "test-thread-123"
196
215
 
197
216
 
198
217
  @pytest.mark.asyncio
199
218
  async def test_context_var_cleanup():
200
219
  """Test that clearing session_id works correctly."""
201
220
  # Set a session_id
202
- veris.set_session_id("test-session")
221
+ token = create_test_token("test-session")
222
+ veris.parse_token(token)
203
223
  assert veris.session_id == "test-session"
204
224
 
205
225
  # Clear it
206
- veris.clear_session_id()
226
+ veris.clear_context()
207
227
  assert veris.session_id is None
208
228
 
209
229
  # Verify it stays None
@@ -217,7 +237,8 @@ async def test_context_var_no_interference():
217
237
  results = []
218
238
 
219
239
  async def task1():
220
- veris.set_session_id("task1-session")
240
+ token = create_test_token("task1-session")
241
+ veris.parse_token(token)
221
242
  await asyncio.sleep(0.02)
222
243
  results.append(("task1", veris.session_id))
223
244
 
@@ -225,7 +246,8 @@ async def test_context_var_no_interference():
225
246
  await asyncio.sleep(0.01)
226
247
  # Should be None since we didn't set it in this context
227
248
  results.append(("task2", veris.session_id))
228
- veris.set_session_id("task2-session")
249
+ token = create_test_token("task2-session")
250
+ veris.parse_token(token)
229
251
  results.append(("task2-after", veris.session_id))
230
252
 
231
253
  await asyncio.gather(task1(), task2())
@@ -251,7 +273,8 @@ async def test_spy_decorator_with_session(simulation_env):
251
273
  patch("veris_ai.tool_mock.log_tool_call") as mock_log_call,
252
274
  patch("veris_ai.tool_mock.log_tool_response") as mock_log_response,
253
275
  ):
254
- veris.set_session_id("spy-session-123")
276
+ token = create_test_token("spy-session-123")
277
+ veris.parse_token(token)
255
278
  result = await test_func(42, "hello")
256
279
 
257
280
  # Verify the original function was executed
@@ -281,7 +304,7 @@ async def test_spy_decorator_without_session():
281
304
  return x * 2
282
305
 
283
306
  # Clear session to ensure no spying
284
- veris.clear_session_id()
307
+ veris.clear_context()
285
308
 
286
309
  with (
287
310
  patch("veris_ai.tool_mock.log_tool_call") as mock_log_call,
@@ -309,7 +332,8 @@ def test_spy_decorator_sync_function(simulation_env):
309
332
  patch("veris_ai.tool_mock.log_tool_call") as mock_log_call,
310
333
  patch("veris_ai.tool_mock.log_tool_response") as mock_log_response,
311
334
  ):
312
- veris.set_session_id("sync-spy-session")
335
+ token = create_test_token("sync-spy-session")
336
+ veris.parse_token(token)
313
337
  result = sync_func(10, 20)
314
338
 
315
339
  # Verify the original function was executed
@@ -335,7 +359,7 @@ def test_spy_decorator_sync_without_session():
335
359
  def sync_func(x: str) -> str:
336
360
  return x.upper()
337
361
 
338
- veris.clear_session_id()
362
+ veris.clear_context()
339
363
 
340
364
  with (
341
365
  patch("veris_ai.tool_mock.log_tool_call") as mock_log_call,
@@ -365,7 +389,8 @@ async def test_spy_decorator_preserves_metadata():
365
389
  assert documented_spy_function.__doc__ == "This is a documented spy function."
366
390
 
367
391
  # Test that it works correctly
368
- veris.set_session_id("test-spy")
392
+ token = create_test_token("test-spy")
393
+ veris.parse_token(token)
369
394
  result = await documented_spy_function(100)
370
395
  assert result == "100: default"
371
396
 
@@ -385,7 +410,8 @@ async def test_spy_decorator_with_exception():
385
410
  patch("veris_ai.tool_mock.log_tool_call") as mock_log_call,
386
411
  patch("veris_ai.tool_mock.log_tool_response") as mock_log_response,
387
412
  ):
388
- veris.set_session_id("exception-spy-session")
413
+ token = create_test_token("exception-spy-session")
414
+ veris.parse_token(token)
389
415
 
390
416
  # Test normal execution
391
417
  result = await failing_func(5)
@@ -16,6 +16,7 @@ if sys.version_info >= (3, 13):
16
16
  from agents import Agent, FunctionTool
17
17
 
18
18
  from veris_ai import veris, Runner, VerisConfig, ToolCallOptions, ResponseExpectation
19
+ from tests.test_helpers import create_test_token
19
20
 
20
21
  # Skip tests if no API key is available
21
22
  if not os.environ.get("OPENAI_API_KEY"):
@@ -131,8 +132,9 @@ def mock_api_client_with_options():
131
132
  @pytest.mark.asyncio
132
133
  async def test_tool_options_passed_to_mock_call(mock_api_client_with_options, multi_tool_agent):
133
134
  """Verify that ToolCallOptions are correctly passed to the mock_tool_call function."""
134
- # Set up session for mocking
135
- veris.set_session_id("test-session-tool-options")
135
+ # Set up session for mocking using proper token format
136
+ token = create_test_token("test-session-tool-options")
137
+ veris.set_session_id(token)
136
138
 
137
139
  try:
138
140
  config = VerisConfig(
@@ -260,16 +260,16 @@ wheels = [
260
260
 
261
261
  [[package]]
262
262
  name = "fastapi"
263
- version = "0.118.0"
263
+ version = "0.118.2"
264
264
  source = { registry = "https://pypi.org/simple" }
265
265
  dependencies = [
266
266
  { name = "pydantic" },
267
267
  { name = "starlette" },
268
268
  { name = "typing-extensions" },
269
269
  ]
270
- sdist = { url = "https://files.pythonhosted.org/packages/28/3c/2b9345a6504e4055eaa490e0b41c10e338ad61d9aeaae41d97807873cdf2/fastapi-0.118.0.tar.gz", hash = "sha256:5e81654d98c4d2f53790a7d32d25a7353b30c81441be7d0958a26b5d761fa1c8", size = 310536, upload-time = "2025-09-29T03:37:23.126Z" }
270
+ sdist = { url = "https://files.pythonhosted.org/packages/2e/ad/31a59efecca3b584440cafac6f69634f4661295c858912c2b2905280a089/fastapi-0.118.2.tar.gz", hash = "sha256:d5388dbe76d97cb6ccd2c93b4dd981608062ebf6335280edfa9a11af82443e18", size = 311963, upload-time = "2025-10-08T14:52:17.796Z" }
271
271
  wheels = [
272
- { url = "https://files.pythonhosted.org/packages/54/20/54e2bdaad22ca91a59455251998d43094d5c3d3567c52c7c04774b3f43f2/fastapi-0.118.0-py3-none-any.whl", hash = "sha256:705137a61e2ef71019d2445b123aa8845bd97273c395b744d5a7dfe559056855", size = 97694, upload-time = "2025-09-29T03:37:21.338Z" },
272
+ { url = "https://files.pythonhosted.org/packages/45/7c/97d033faf771c9fe960c7b51eb78ab266bfa64cbc917601978963f0c3c7b/fastapi-0.118.2-py3-none-any.whl", hash = "sha256:d1f842612e6a305f95abe784b7f8d3215477742e7c67a16fccd20bd79db68150", size = 97954, upload-time = "2025-10-08T14:52:16.166Z" },
273
273
  ]
274
274
 
275
275
  [[package]]
@@ -295,11 +295,11 @@ wheels = [
295
295
 
296
296
  [[package]]
297
297
  name = "filelock"
298
- version = "3.19.1"
298
+ version = "3.20.0"
299
299
  source = { registry = "https://pypi.org/simple" }
300
- sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" }
300
+ sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" }
301
301
  wheels = [
302
- { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" },
302
+ { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" },
303
303
  ]
304
304
 
305
305
  [[package]]
@@ -936,11 +936,11 @@ wheels = [
936
936
 
937
937
  [[package]]
938
938
  name = "platformdirs"
939
- version = "4.4.0"
939
+ version = "4.5.0"
940
940
  source = { registry = "https://pypi.org/simple" }
941
- sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" }
941
+ sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" }
942
942
  wheels = [
943
- { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" },
943
+ { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" },
944
944
  ]
945
945
 
946
946
  [[package]]
@@ -1571,7 +1571,7 @@ wheels = [
1571
1571
 
1572
1572
  [[package]]
1573
1573
  name = "veris-ai"
1574
- version = "1.11.0"
1574
+ version = "1.11.1"
1575
1575
  source = { editable = "." }
1576
1576
  dependencies = [
1577
1577
  { name = "httpx" },
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