latitude-sdk 1.0.1__tar.gz → 1.0.3__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 (43) hide show
  1. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/PKG-INFO +24 -2
  2. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/README.md +22 -0
  3. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/pyproject.toml +2 -2
  4. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/sdk/latitude.py +1 -1
  5. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/sdk/prompts.py +40 -56
  6. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/sdk/types.py +86 -45
  7. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/prompts/chat_test.py +28 -28
  8. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/prompts/run_test.py +50 -50
  9. latitude_sdk-1.0.3/tests/utils/fixtures.py +1502 -0
  10. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/utils/utils.py +2 -2
  11. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/uv.lock +236 -233
  12. latitude_sdk-1.0.1/tests/utils/fixtures.py +0 -998
  13. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/.gitignore +0 -0
  14. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/.python-version +0 -0
  15. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/scripts/format.py +0 -0
  16. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/scripts/lint.py +0 -0
  17. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/scripts/test.py +0 -0
  18. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/__init__.py +0 -0
  19. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/client/__init__.py +0 -0
  20. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/client/client.py +0 -0
  21. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/client/payloads.py +0 -0
  22. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/client/router.py +0 -0
  23. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/env/__init__.py +0 -0
  24. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/env/env.py +0 -0
  25. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/py.typed +0 -0
  26. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/sdk/__init__.py +0 -0
  27. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/sdk/errors.py +0 -0
  28. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/sdk/evaluations.py +0 -0
  29. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/sdk/logs.py +0 -0
  30. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/util/__init__.py +0 -0
  31. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/src/latitude_sdk/util/utils.py +0 -0
  32. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/__init__.py +0 -0
  33. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/evaluations/__init__.py +0 -0
  34. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/evaluations/create_result_test.py +0 -0
  35. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/evaluations/trigger_test.py +0 -0
  36. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/logs/__init__.py +0 -0
  37. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/logs/create_test.py +0 -0
  38. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/prompts/__init__.py +0 -0
  39. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/prompts/get_or_create_test.py +0 -0
  40. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/prompts/get_test.py +0 -0
  41. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/prompts/render_chain_test.py +0 -0
  42. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/prompts/render_test.py +0 -0
  43. {latitude_sdk-1.0.1 → latitude_sdk-1.0.3}/tests/utils/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: latitude-sdk
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: Latitude SDK for Python
5
5
  Project-URL: repository, https://github.com/latitude-dev/latitude-llm/tree/main/packages/sdks/python
6
6
  Project-URL: homepage, https://github.com/latitude-dev/latitude-llm/tree/main/packages/sdks/python#readme
@@ -12,7 +12,7 @@ Requires-Python: <3.13,>=3.9
12
12
  Requires-Dist: httpx-sse>=0.4.0
13
13
  Requires-Dist: httpx>=0.28.1
14
14
  Requires-Dist: latitude-telemetry>=1.0.0
15
- Requires-Dist: promptl-ai>=1.0.1
15
+ Requires-Dist: promptl-ai>=1.0.2
16
16
  Requires-Dist: pydantic>=2.10.3
17
17
  Requires-Dist: typing-extensions>=4.12.2
18
18
  Description-Content-Type: text/markdown
@@ -60,6 +60,28 @@ Requires uv `0.5.10` or higher.
60
60
  - Build package: `uv build`
61
61
  - Publish package: `uv publish`
62
62
 
63
+ ## Run only one test
64
+
65
+ ```python
66
+ import pytest
67
+
68
+ @pytest.mark.only
69
+ async def my_test(self):
70
+ # ... your code
71
+ ```
72
+
73
+ And then run the tests with the marker `only`:
74
+
75
+ ```sh
76
+ uv run scripts/test.py -m only
77
+ ```
78
+
79
+ Other way is all in line:
80
+
81
+ ```python
82
+ uv run scripts/test.py <test_path>::<test_case>::<test_name>
83
+ ```
84
+
63
85
  ## License
64
86
 
65
87
  The SDK is licensed under the [LGPL-3.0 License](https://opensource.org/licenses/LGPL-3.0) - read the [LICENSE](/LICENSE) file for details.
@@ -41,6 +41,28 @@ Requires uv `0.5.10` or higher.
41
41
  - Build package: `uv build`
42
42
  - Publish package: `uv publish`
43
43
 
44
+ ## Run only one test
45
+
46
+ ```python
47
+ import pytest
48
+
49
+ @pytest.mark.only
50
+ async def my_test(self):
51
+ # ... your code
52
+ ```
53
+
54
+ And then run the tests with the marker `only`:
55
+
56
+ ```sh
57
+ uv run scripts/test.py -m only
58
+ ```
59
+
60
+ Other way is all in line:
61
+
62
+ ```python
63
+ uv run scripts/test.py <test_path>::<test_case>::<test_name>
64
+ ```
65
+
44
66
  ## License
45
67
 
46
68
  The SDK is licensed under the [LGPL-3.0 License](https://opensource.org/licenses/LGPL-3.0) - read the [LICENSE](/LICENSE) file for details.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "latitude-sdk"
3
- version = "1.0.1"
3
+ version = "1.0.3"
4
4
  description = "Latitude SDK for Python"
5
5
  authors = [{ name = "Latitude Data SL", email = "hello@latitude.so" }]
6
6
  maintainers = [{ name = "Latitude Data SL", email = "hello@latitude.so" }]
@@ -16,7 +16,7 @@ dependencies = [
16
16
  "pydantic>=2.10.3",
17
17
  "typing-extensions>=4.12.2",
18
18
  "latitude-telemetry>=1.0.0",
19
- "promptl-ai>=1.0.1",
19
+ "promptl-ai>=1.0.2",
20
20
  ]
21
21
 
22
22
  [dependency-groups]
@@ -31,7 +31,7 @@ DEFAULT_INTERNAL_OPTIONS = InternalOptions(
31
31
  host=env.GATEWAY_HOSTNAME,
32
32
  port=env.GATEWAY_PORT,
33
33
  ssl=env.GATEWAY_SSL,
34
- api_version="v2",
34
+ api_version="v3",
35
35
  ),
36
36
  source=LogSources.Api,
37
37
  retries=3,
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- from typing import Any, AsyncGenerator, Dict, List, Optional, Sequence, Union
2
+ from typing import Any, AsyncGenerator, Optional, Sequence, Union
3
3
 
4
4
  from promptl_ai import Adapter, Message, MessageLike, Promptl, ToolMessage, ToolResultContent
5
5
  from promptl_ai.bindings.types import _Message
@@ -18,12 +18,8 @@ from latitude_sdk.client import (
18
18
  )
19
19
  from latitude_sdk.sdk.errors import ApiError, ApiErrorCodes
20
20
  from latitude_sdk.sdk.types import (
21
- ChainEventCompleted,
22
- ChainEventError,
23
21
  ChainEvents,
24
- ChainEventStep,
25
- ChainEventStepCompleted,
26
- FinishedEvent,
22
+ FinishedResult,
27
23
  OnStep,
28
24
  OnToolCall,
29
25
  OnToolCallDetails,
@@ -33,7 +29,9 @@ from latitude_sdk.sdk.types import (
33
29
  StreamCallbacks,
34
30
  StreamEvents,
35
31
  StreamTypes,
32
+ ToolCall,
36
33
  ToolResult,
34
+ _LatitudeEvent,
37
35
  )
38
36
  from latitude_sdk.util import Model
39
37
 
@@ -79,36 +77,36 @@ class GetOrCreatePromptResult(Prompt, Model):
79
77
 
80
78
  class RunPromptOptions(StreamCallbacks, PromptOptions, Model):
81
79
  custom_identifier: Optional[str] = None
82
- parameters: Optional[Dict[str, Any]] = None
83
- tools: Optional[Dict[str, OnToolCall]] = None
80
+ parameters: Optional[dict[str, Any]] = None
81
+ tools: Optional[dict[str, OnToolCall]] = None
84
82
  stream: Optional[bool] = None
85
83
 
86
84
 
87
- class RunPromptResult(FinishedEvent, Model):
85
+ class RunPromptResult(FinishedResult, Model):
88
86
  pass
89
87
 
90
88
 
91
89
  class ChatPromptOptions(StreamCallbacks, Model):
92
- tools: Optional[Dict[str, OnToolCall]] = None
90
+ tools: Optional[dict[str, OnToolCall]] = None
93
91
  stream: Optional[bool] = None
94
92
 
95
93
 
96
- class ChatPromptResult(FinishedEvent, Model):
94
+ class ChatPromptResult(FinishedResult, Model):
97
95
  pass
98
96
 
99
97
 
100
98
  class RenderPromptOptions(Model):
101
- parameters: Optional[Dict[str, Any]] = None
99
+ parameters: Optional[dict[str, Any]] = None
102
100
  adapter: Optional[Adapter] = None
103
101
 
104
102
 
105
103
  class RenderPromptResult(Model):
106
- messages: List[MessageLike]
107
- config: Dict[str, Any]
104
+ messages: list[MessageLike]
105
+ config: dict[str, Any]
108
106
 
109
107
 
110
108
  class RenderChainOptions(Model):
111
- parameters: Optional[Dict[str, Any]] = None
109
+ parameters: Optional[dict[str, Any]] = None
112
110
  adapter: Optional[Adapter] = None
113
111
 
114
112
 
@@ -137,32 +135,27 @@ class Prompts:
137
135
 
138
136
  async def _handle_stream(
139
137
  self, stream: AsyncGenerator[ClientEvent, Any], on_event: Optional[StreamCallbacks.OnEvent]
140
- ) -> FinishedEvent:
138
+ ) -> FinishedResult:
141
139
  uuid = None
142
- conversation: List[Message] = []
140
+ conversation: list[Message] = []
143
141
  response = None
142
+ tool_requests: list[ToolCall] = []
144
143
 
145
144
  async for stream_event in stream:
146
145
  event = None
147
146
 
148
147
  if stream_event.event == str(StreamEvents.Latitude):
149
- type = stream_event.json().get("type")
148
+ event = _LatitudeEvent.validate_json(stream_event.data)
149
+ conversation = event.messages
150
+ uuid = event.uuid
150
151
 
151
- if type == str(ChainEvents.Step):
152
- event = ChainEventStep.model_validate_json(stream_event.data)
153
- conversation.extend(event.messages)
154
-
155
- elif type == str(ChainEvents.StepCompleted):
156
- event = ChainEventStepCompleted.model_validate_json(stream_event.data)
157
-
158
- elif type == str(ChainEvents.Completed):
159
- event = ChainEventCompleted.model_validate_json(stream_event.data)
160
- uuid = event.uuid
161
- conversation.extend(event.messages or [])
152
+ if event.type == ChainEvents.ProviderCompleted:
162
153
  response = event.response
163
154
 
164
- elif type == str(ChainEvents.Error):
165
- event = ChainEventError.model_validate_json(stream_event.data)
155
+ elif event.type == ChainEvents.ToolsRequested:
156
+ tool_requests = event.tools
157
+
158
+ elif event.type == ChainEvents.ChainError:
166
159
  raise ApiError(
167
160
  status=400,
168
161
  code=ApiErrorCodes.AIRunError,
@@ -170,14 +163,6 @@ class Prompts:
170
163
  response=stream_event.data,
171
164
  )
172
165
 
173
- else:
174
- raise ApiError(
175
- status=500,
176
- code=ApiErrorCodes.InternalServerError,
177
- message=f"Unknown latitude event: {type}",
178
- response=stream_event.data,
179
- )
180
-
181
166
  elif stream_event.event == str(StreamEvents.Provider):
182
167
  event = stream_event.json()
183
168
  event["event"] = StreamEvents.Provider
@@ -201,8 +186,7 @@ class Prompts:
201
186
  response="Stream ended without a chain-complete event. Missing uuid or response.",
202
187
  )
203
188
 
204
- # NOTE: FinishedEvent not in on_event
205
- return FinishedEvent(uuid=uuid, conversation=conversation, response=response)
189
+ return FinishedResult(uuid=uuid, conversation=conversation, response=response, tool_requests=tool_requests)
206
190
 
207
191
  @staticmethod
208
192
  def _pause_tool_execution() -> Any:
@@ -210,9 +194,9 @@ class Prompts:
210
194
 
211
195
  @staticmethod
212
196
  async def _wrap_tool_handler(
213
- handler: OnToolCall, arguments: Dict[str, Any], details: OnToolCallDetails
197
+ handler: OnToolCall, arguments: dict[str, Any], details: OnToolCallDetails
214
198
  ) -> ToolResult:
215
- tool_result: Dict[str, Any] = {"id": details.id, "name": details.name}
199
+ tool_result: dict[str, Any] = {"id": details.id, "name": details.name}
216
200
 
217
201
  try:
218
202
  result = await handler(arguments, details)
@@ -225,10 +209,10 @@ class Prompts:
225
209
  return ToolResult(**tool_result, result=str(exception), is_error=True)
226
210
 
227
211
  async def _handle_tool_calls(
228
- self, result: FinishedEvent, options: Union[RunPromptOptions, ChatPromptOptions]
229
- ) -> Optional[FinishedEvent]:
212
+ self, result: FinishedResult, options: Union[RunPromptOptions, ChatPromptOptions]
213
+ ) -> Optional[FinishedResult]:
230
214
  # Seems Python cannot infer the type
231
- assert result.response.type == StreamTypes.Text and result.response.tool_calls is not None
215
+ assert result.response.type == StreamTypes.Text and result.tool_requests is not None
232
216
 
233
217
  if not options.tools:
234
218
  raise ApiError(
@@ -238,7 +222,7 @@ class Prompts:
238
222
  response="Tools not supplied",
239
223
  )
240
224
 
241
- for tool_call in result.response.tool_calls:
225
+ for tool_call in result.tool_requests:
242
226
  if tool_call.name not in options.tools:
243
227
  raise ApiError(
244
228
  status=400,
@@ -258,10 +242,10 @@ class Prompts:
258
242
  conversation_uuid=result.uuid,
259
243
  messages=result.conversation,
260
244
  pause_execution=self._pause_tool_execution,
261
- requested_tool_calls=result.response.tool_calls,
245
+ requested_tool_calls=result.tool_requests,
262
246
  ),
263
247
  )
264
- for tool_call in result.response.tool_calls
248
+ for tool_call in result.tool_requests
265
249
  ],
266
250
  return_exceptions=False,
267
251
  )
@@ -282,7 +266,7 @@ class Prompts:
282
266
 
283
267
  next_result = await self.chat(result.uuid, tool_messages, ChatPromptOptions(**dict(options)))
284
268
 
285
- return FinishedEvent(**dict(next_result)) if next_result else None
269
+ return FinishedResult(**dict(next_result)) if next_result else None
286
270
 
287
271
  async def get(self, path: str, options: Optional[GetPromptOptions] = None) -> GetPromptResult:
288
272
  options = GetPromptOptions(**{**dict(self._options), **dict(options or {})})
@@ -343,7 +327,7 @@ class Prompts:
343
327
  else:
344
328
  result = RunPromptResult.model_validate_json(response.content)
345
329
 
346
- if options.tools and result.response.type == StreamTypes.Text and result.response.tool_calls:
330
+ if options.tools and result.response.type == StreamTypes.Text and result.tool_requests:
347
331
  try:
348
332
  # NOTE: The last sdk.chat called will already call on_finished
349
333
  final_result = await self._handle_tool_calls(result, options)
@@ -352,7 +336,7 @@ class Prompts:
352
336
  pass
353
337
 
354
338
  if options.on_finished:
355
- options.on_finished(FinishedEvent(**dict(result)))
339
+ options.on_finished(FinishedResult(**dict(result)))
356
340
 
357
341
  return RunPromptResult(**dict(result))
358
342
 
@@ -395,7 +379,7 @@ class Prompts:
395
379
  else:
396
380
  result = ChatPromptResult.model_validate_json(response.content)
397
381
 
398
- if options.tools and result.response.type == StreamTypes.Text and result.response.tool_calls:
382
+ if options.tools and result.response.type == StreamTypes.Text and result.tool_requests:
399
383
  try:
400
384
  # NOTE: The last sdk.chat called will already call on_finished
401
385
  final_result = await self._handle_tool_calls(result, options)
@@ -404,7 +388,7 @@ class Prompts:
404
388
  pass
405
389
 
406
390
  if options.on_finished:
407
- options.on_finished(FinishedEvent(**dict(result)))
391
+ options.on_finished(FinishedResult(**dict(result)))
408
392
 
409
393
  return ChatPromptResult(**dict(result))
410
394
 
@@ -424,8 +408,8 @@ class Prompts:
424
408
 
425
409
  return None
426
410
 
427
- def _adapt_prompt_config(self, config: Dict[str, Any], adapter: Adapter) -> Dict[str, Any]:
428
- adapted_config: Dict[str, Any] = {}
411
+ def _adapt_prompt_config(self, config: dict[str, Any], adapter: Adapter) -> dict[str, Any]:
412
+ adapted_config: dict[str, Any] = {}
429
413
 
430
414
  # NOTE: Should we delete attributes not supported by the provider?
431
415
  for attr, value in config.items():
@@ -1,10 +1,10 @@
1
1
  from datetime import datetime
2
- from typing import Any, Callable, Dict, List, Literal, Optional, Protocol, Sequence, Union, runtime_checkable
2
+ from typing import Any, Callable, Literal, Optional, Protocol, Sequence, Union, runtime_checkable
3
3
 
4
4
  from promptl_ai import Message, MessageLike
5
5
 
6
6
  from latitude_sdk.sdk.errors import ApiError
7
- from latitude_sdk.util import Field, Model, StrEnum
7
+ from latitude_sdk.util import Adapter, Field, Model, StrEnum
8
8
 
9
9
 
10
10
  class DbErrorRef(Model):
@@ -19,6 +19,8 @@ class Providers(StrEnum):
19
19
  Mistral = "mistral"
20
20
  Azure = "azure"
21
21
  Google = "google"
22
+ GoogleVertex = "google_vertex"
23
+ AnthropicVertex = "anthropic_vertex"
22
24
  Custom = "custom"
23
25
 
24
26
 
@@ -26,7 +28,7 @@ class Prompt(Model):
26
28
  uuid: str
27
29
  path: str
28
30
  content: str
29
- config: Dict[str, Any]
31
+ config: dict[str, Any]
30
32
  provider: Optional[Providers] = None
31
33
 
32
34
 
@@ -49,7 +51,7 @@ class FinishReason(StrEnum):
49
51
  class ToolCall(Model):
50
52
  id: str
51
53
  name: str
52
- arguments: Dict[str, Any]
54
+ arguments: dict[str, Any]
53
55
 
54
56
 
55
57
  class ToolResult(Model):
@@ -67,7 +69,7 @@ class StreamTypes(StrEnum):
67
69
  class ChainTextResponse(Model):
68
70
  type: Literal[StreamTypes.Text] = Field(default=StreamTypes.Text, alias=str("streamType"))
69
71
  text: str
70
- tool_calls: Optional[List[ToolCall]] = Field(default=None, alias=str("toolCalls"))
72
+ tool_calls: list[ToolCall] = Field(alias=str("toolCalls"))
71
73
  usage: ModelUsage
72
74
 
73
75
 
@@ -89,66 +91,105 @@ class ChainError(Model):
89
91
  class StreamEvents(StrEnum):
90
92
  Latitude = "latitude-event"
91
93
  Provider = "provider-event"
92
- Finished = "finished-event"
93
94
 
94
95
 
95
- ProviderEvent = Dict[str, Any]
96
+ ProviderEvent = dict[str, Any]
96
97
 
97
98
 
98
99
  class ChainEvents(StrEnum):
99
- Step = "chain-step"
100
- StepCompleted = "chain-step-complete"
101
- Completed = "chain-complete"
102
- Error = "chain-error"
100
+ ChainStarted = "chain-started"
101
+ StepStarted = "step-started"
102
+ ProviderStarted = "provider-started"
103
+ ProviderCompleted = "provider-completed"
104
+ ToolsStarted = "tools-started"
105
+ ToolCompleted = "tool-completed"
106
+ StepCompleted = "step-completed"
107
+ ChainCompleted = "chain-completed"
108
+ ChainError = "chain-error"
109
+ ToolsRequested = "tools-requested"
110
+
111
+
112
+ class GenericChainEvent(Model):
113
+ event: Literal[StreamEvents.Latitude] = StreamEvents.Latitude
114
+ messages: list[Message]
115
+ uuid: str
103
116
 
104
117
 
105
- class ChainEventStep(Model):
106
- event: Literal[StreamEvents.Latitude] = StreamEvents.Latitude
107
- type: Literal[ChainEvents.Step] = ChainEvents.Step
108
- uuid: Optional[str] = None
109
- is_last_step: bool = Field(alias=str("isLastStep"))
110
- config: Dict[str, Any]
111
- messages: List[Message]
118
+ class ChainEventChainStarted(GenericChainEvent):
119
+ type: Literal[ChainEvents.ChainStarted] = ChainEvents.ChainStarted
112
120
 
113
121
 
114
- class ChainEventStepCompleted(Model):
115
- event: Literal[StreamEvents.Latitude] = StreamEvents.Latitude
116
- type: Literal[ChainEvents.StepCompleted] = ChainEvents.StepCompleted
117
- uuid: Optional[str] = None
118
- response: ChainResponse
122
+ class ChainEventStepStarted(GenericChainEvent):
123
+ type: Literal[ChainEvents.StepStarted] = ChainEvents.StepStarted
119
124
 
120
125
 
121
- class ChainEventCompleted(Model):
122
- event: Literal[StreamEvents.Latitude] = StreamEvents.Latitude
123
- type: Literal[ChainEvents.Completed] = ChainEvents.Completed
124
- uuid: Optional[str] = None
126
+ class ChainEventProviderStarted(GenericChainEvent):
127
+ type: Literal[ChainEvents.ProviderStarted] = ChainEvents.ProviderStarted
128
+ config: dict[str, Any]
129
+
130
+
131
+ class ChainEventProviderCompleted(GenericChainEvent):
132
+ type: Literal[ChainEvents.ProviderCompleted] = ChainEvents.ProviderCompleted
133
+ provider_log_uuid: str = Field(alias=str("providerLogUuid"))
134
+ token_usage: ModelUsage = Field(alias=str("tokenUsage"))
125
135
  finish_reason: FinishReason = Field(alias=str("finishReason"))
126
- config: Dict[str, Any]
127
- messages: Optional[List[Message]] = None
128
- object: Optional[Any] = None
129
136
  response: ChainResponse
130
137
 
131
138
 
132
- class ChainEventError(Model):
133
- event: Literal[StreamEvents.Latitude] = StreamEvents.Latitude
134
- type: Literal[ChainEvents.Error] = ChainEvents.Error
139
+ class ChainEventToolsStarted(GenericChainEvent):
140
+ type: Literal[ChainEvents.ToolsStarted] = ChainEvents.ToolsStarted
141
+ tools: list[ToolCall]
142
+
143
+
144
+ class ChainEventToolCompleted(GenericChainEvent):
145
+ type: Literal[ChainEvents.ToolCompleted] = ChainEvents.ToolCompleted
146
+
147
+
148
+ class ChainEventStepCompleted(GenericChainEvent):
149
+ type: Literal[ChainEvents.StepCompleted] = ChainEvents.StepCompleted
150
+
151
+
152
+ class ChainEventChainCompleted(GenericChainEvent):
153
+ type: Literal[ChainEvents.ChainCompleted] = ChainEvents.ChainCompleted
154
+ token_usage: ModelUsage = Field(alias=str("tokenUsage"))
155
+ finish_reason: FinishReason = Field(alias=str("finishReason"))
156
+
157
+
158
+ class ChainEventChainError(GenericChainEvent):
159
+ type: Literal[ChainEvents.ChainError] = ChainEvents.ChainError
135
160
  error: ChainError
136
161
 
137
162
 
138
- ChainEvent = Union[ChainEventStep, ChainEventStepCompleted, ChainEventCompleted, ChainEventError]
163
+ class ChainEventToolsRequested(GenericChainEvent):
164
+ type: Literal[ChainEvents.ToolsRequested] = ChainEvents.ToolsRequested
165
+ tools: list[ToolCall]
166
+
139
167
 
168
+ ChainEvent = Union[
169
+ ChainEventChainStarted,
170
+ ChainEventStepStarted,
171
+ ChainEventProviderStarted,
172
+ ChainEventProviderCompleted,
173
+ ChainEventToolsStarted,
174
+ ChainEventToolCompleted,
175
+ ChainEventStepCompleted,
176
+ ChainEventChainCompleted,
177
+ ChainEventChainError,
178
+ ChainEventToolsRequested,
179
+ ]
140
180
 
141
181
  LatitudeEvent = ChainEvent
182
+ _LatitudeEvent = Adapter[LatitudeEvent](LatitudeEvent)
142
183
 
143
184
 
144
- class FinishedEvent(Model):
145
- event: Literal[StreamEvents.Finished] = StreamEvents.Finished
185
+ class FinishedResult(Model):
146
186
  uuid: str
147
- conversation: List[Message]
187
+ conversation: list[Message]
148
188
  response: ChainResponse
189
+ tool_requests: list[ToolCall] = Field(alias=str("toolRequests"))
149
190
 
150
191
 
151
- StreamEvent = Union[ProviderEvent, LatitudeEvent, FinishedEvent]
192
+ StreamEvent = Union[ProviderEvent, LatitudeEvent]
152
193
 
153
194
 
154
195
  class LogSources(StrEnum):
@@ -166,7 +207,7 @@ class Log(Model):
166
207
  commit_id: int = Field(alias=str("commitId"))
167
208
  resolved_content: str = Field(alias=str("resolvedContent"))
168
209
  content_hash: str = Field(alias=str("contentHash"))
169
- parameters: Dict[str, Any]
210
+ parameters: dict[str, Any]
170
211
  custom_identifier: Optional[str] = Field(default=None, alias=str("customIdentifier"))
171
212
  duration: Optional[int] = None
172
213
  created_at: datetime = Field(alias=str("createdAt"))
@@ -204,7 +245,7 @@ class StreamCallbacks(Model):
204
245
 
205
246
  @runtime_checkable
206
247
  class OnFinished(Protocol):
207
- def __call__(self, event: FinishedEvent): ...
248
+ def __call__(self, result: FinishedResult): ...
208
249
 
209
250
  on_finished: Optional[OnFinished] = None
210
251
 
@@ -219,27 +260,27 @@ class OnToolCallDetails(Model):
219
260
  id: str
220
261
  name: str
221
262
  conversation_uuid: str
222
- messages: List[Message]
263
+ messages: list[Message]
223
264
  pause_execution: Callable[[], ToolResult]
224
- requested_tool_calls: List[ToolCall]
265
+ requested_tool_calls: list[ToolCall]
225
266
 
226
267
 
227
268
  @runtime_checkable
228
269
  class OnToolCall(Protocol):
229
- async def __call__(self, arguments: Dict[str, Any], details: OnToolCallDetails) -> Any: ...
270
+ async def __call__(self, arguments: dict[str, Any], details: OnToolCallDetails) -> Any: ...
230
271
 
231
272
 
232
273
  @runtime_checkable
233
274
  class OnStep(Protocol):
234
275
  async def __call__(
235
- self, messages: List[MessageLike], config: Dict[str, Any]
276
+ self, messages: list[MessageLike], config: dict[str, Any]
236
277
  ) -> Union[str, MessageLike, Sequence[MessageLike]]: ...
237
278
 
238
279
 
239
280
  class SdkOptions(Model):
240
281
  project_id: Optional[int] = None
241
282
  version_uuid: Optional[str] = None
242
- tools: Optional[Dict[str, OnToolCall]] = None
283
+ tools: Optional[dict[str, OnToolCall]] = None
243
284
 
244
285
 
245
286
  class GatewayOptions(Model):