latitude-sdk 0.1.0b1__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.
- latitude_sdk/__init__.py +1 -0
- latitude_sdk/client/__init__.py +3 -0
- latitude_sdk/client/client.py +140 -0
- latitude_sdk/client/payloads.py +117 -0
- latitude_sdk/client/router.py +117 -0
- latitude_sdk/env/__init__.py +1 -0
- latitude_sdk/env/env.py +18 -0
- latitude_sdk/py.typed +0 -0
- latitude_sdk/sdk/__init__.py +6 -0
- latitude_sdk/sdk/errors.py +59 -0
- latitude_sdk/sdk/evaluations.py +69 -0
- latitude_sdk/sdk/latitude.py +76 -0
- latitude_sdk/sdk/logs.py +66 -0
- latitude_sdk/sdk/prompts.py +297 -0
- latitude_sdk/sdk/types.py +296 -0
- latitude_sdk/util/__init__.py +1 -0
- latitude_sdk/util/utils.py +87 -0
- latitude_sdk-0.1.0b1.dist-info/METADATA +63 -0
- latitude_sdk-0.1.0b1.dist-info/RECORD +20 -0
- latitude_sdk-0.1.0b1.dist-info/WHEEL +4 -0
@@ -0,0 +1,297 @@
|
|
1
|
+
from typing import Any, AsyncGenerator, Dict, List, Optional
|
2
|
+
|
3
|
+
from latitude_sdk.client import (
|
4
|
+
ChatPromptRequestBody,
|
5
|
+
ChatPromptRequestParams,
|
6
|
+
Client,
|
7
|
+
ClientEvent,
|
8
|
+
GetOrCreatePromptRequestBody,
|
9
|
+
GetOrCreatePromptRequestParams,
|
10
|
+
GetPromptRequestParams,
|
11
|
+
RequestHandler,
|
12
|
+
RunPromptRequestBody,
|
13
|
+
RunPromptRequestParams,
|
14
|
+
)
|
15
|
+
from latitude_sdk.sdk.errors import ApiError, ApiErrorCodes
|
16
|
+
from latitude_sdk.sdk.types import (
|
17
|
+
ChainEventCompleted,
|
18
|
+
ChainEventError,
|
19
|
+
ChainEvents,
|
20
|
+
ChainEventStep,
|
21
|
+
ChainEventStepCompleted,
|
22
|
+
FinishedEvent,
|
23
|
+
Message,
|
24
|
+
Prompt,
|
25
|
+
SdkOptions,
|
26
|
+
StreamCallbacks,
|
27
|
+
StreamEvents,
|
28
|
+
)
|
29
|
+
from latitude_sdk.util import Model
|
30
|
+
|
31
|
+
|
32
|
+
class PromptOptions(Model):
|
33
|
+
project_id: Optional[int] = None
|
34
|
+
version_uuid: Optional[str] = None
|
35
|
+
|
36
|
+
|
37
|
+
class GetPromptOptions(PromptOptions, Model):
|
38
|
+
pass
|
39
|
+
|
40
|
+
|
41
|
+
class GetPromptResult(Prompt, Model):
|
42
|
+
pass
|
43
|
+
|
44
|
+
|
45
|
+
class GetOrCreatePromptOptions(PromptOptions, Model):
|
46
|
+
prompt: Optional[str] = None
|
47
|
+
|
48
|
+
|
49
|
+
class GetOrCreatePromptResult(Prompt, Model):
|
50
|
+
pass
|
51
|
+
|
52
|
+
|
53
|
+
class RunPromptOptions(StreamCallbacks, PromptOptions, Model):
|
54
|
+
custom_identifier: Optional[str] = None
|
55
|
+
parameters: Optional[Dict[str, Any]] = None
|
56
|
+
stream: Optional[bool] = None
|
57
|
+
|
58
|
+
|
59
|
+
class RunPromptResult(FinishedEvent, Model):
|
60
|
+
pass
|
61
|
+
|
62
|
+
|
63
|
+
class ChatPromptOptions(StreamCallbacks, Model):
|
64
|
+
stream: Optional[bool] = None
|
65
|
+
|
66
|
+
|
67
|
+
class ChatPromptResult(FinishedEvent, Model):
|
68
|
+
pass
|
69
|
+
|
70
|
+
|
71
|
+
class Prompts:
|
72
|
+
_options: SdkOptions
|
73
|
+
_client: Client
|
74
|
+
|
75
|
+
def __init__(self, client: Client, options: SdkOptions):
|
76
|
+
self._options = options
|
77
|
+
self._client = client
|
78
|
+
|
79
|
+
def _ensure_options(self, options: PromptOptions) -> PromptOptions:
|
80
|
+
project_id = options.project_id or self._options.project_id
|
81
|
+
if not project_id:
|
82
|
+
raise ApiError(
|
83
|
+
status=404,
|
84
|
+
code=ApiErrorCodes.NotFoundError,
|
85
|
+
message="Project ID is required",
|
86
|
+
response="Project ID is required",
|
87
|
+
)
|
88
|
+
|
89
|
+
version_uuid = options.version_uuid or self._options.version_uuid
|
90
|
+
|
91
|
+
return PromptOptions(project_id=project_id, version_uuid=version_uuid)
|
92
|
+
|
93
|
+
async def _handle_stream(
|
94
|
+
self, stream: AsyncGenerator[ClientEvent, Any], callbacks: StreamCallbacks
|
95
|
+
) -> FinishedEvent:
|
96
|
+
uuid = None
|
97
|
+
conversation: List[Message] = []
|
98
|
+
response = None
|
99
|
+
|
100
|
+
async for stream_event in stream:
|
101
|
+
event = None
|
102
|
+
|
103
|
+
if stream_event.event == str(StreamEvents.Latitude):
|
104
|
+
type = stream_event.json().get("type")
|
105
|
+
|
106
|
+
if type == str(ChainEvents.Step):
|
107
|
+
event = ChainEventStep.model_validate_json(stream_event.data)
|
108
|
+
conversation.extend(event.messages)
|
109
|
+
|
110
|
+
elif type == str(ChainEvents.StepCompleted):
|
111
|
+
event = ChainEventStepCompleted.model_validate_json(stream_event.data)
|
112
|
+
|
113
|
+
elif type == str(ChainEvents.Completed):
|
114
|
+
event = ChainEventCompleted.model_validate_json(stream_event.data)
|
115
|
+
uuid = event.uuid
|
116
|
+
conversation.extend(event.messages or [])
|
117
|
+
response = event.response
|
118
|
+
|
119
|
+
elif type == str(ChainEvents.Error):
|
120
|
+
event = ChainEventError.model_validate_json(stream_event.data)
|
121
|
+
raise ApiError(
|
122
|
+
status=500,
|
123
|
+
code=ApiErrorCodes.AIRunError,
|
124
|
+
message=event.error.message,
|
125
|
+
response=stream_event.data,
|
126
|
+
)
|
127
|
+
|
128
|
+
else:
|
129
|
+
raise ApiError(
|
130
|
+
status=500,
|
131
|
+
code=ApiErrorCodes.InternalServerError,
|
132
|
+
message=f"Unknown latitude event: {type}",
|
133
|
+
response=f"Unknown latitude event: {type}",
|
134
|
+
)
|
135
|
+
|
136
|
+
elif stream_event.event == str(StreamEvents.Provider):
|
137
|
+
event = stream_event.json()
|
138
|
+
event["event"] = StreamEvents.Provider
|
139
|
+
|
140
|
+
else:
|
141
|
+
raise ApiError(
|
142
|
+
status=500,
|
143
|
+
code=ApiErrorCodes.InternalServerError,
|
144
|
+
message=f"Unknown stream event: {stream_event.event}",
|
145
|
+
response=f"Unknown stream event: {stream_event.event}",
|
146
|
+
)
|
147
|
+
|
148
|
+
if callbacks.on_event:
|
149
|
+
callbacks.on_event(event)
|
150
|
+
|
151
|
+
if not uuid or not response:
|
152
|
+
raise ApiError(
|
153
|
+
status=500,
|
154
|
+
code=ApiErrorCodes.InternalServerError,
|
155
|
+
message="Stream ended without a chain-complete event. Missing uuid or response.",
|
156
|
+
response="Stream ended without a chain-complete event. Missing uuid or response.",
|
157
|
+
)
|
158
|
+
|
159
|
+
# NOTE: FinishedEvent not in on_event
|
160
|
+
return FinishedEvent(uuid=uuid, conversation=conversation, response=response)
|
161
|
+
|
162
|
+
async def get(self, path: str, options: GetPromptOptions) -> GetPromptResult:
|
163
|
+
prompt_options = self._ensure_options(options)
|
164
|
+
options = GetPromptOptions(**{**dict(options), **dict(prompt_options)})
|
165
|
+
|
166
|
+
assert options.project_id is not None
|
167
|
+
|
168
|
+
async with self._client.request(
|
169
|
+
handler=RequestHandler.GetPrompt,
|
170
|
+
params=GetPromptRequestParams(
|
171
|
+
project_id=options.project_id,
|
172
|
+
version_uuid=options.version_uuid,
|
173
|
+
path=path,
|
174
|
+
),
|
175
|
+
) as response:
|
176
|
+
return GetPromptResult.model_validate_json(response.content)
|
177
|
+
|
178
|
+
async def get_or_create(self, path: str, options: GetOrCreatePromptOptions) -> GetOrCreatePromptResult:
|
179
|
+
prompt_options = self._ensure_options(options)
|
180
|
+
options = GetOrCreatePromptOptions(**{**dict(options), **dict(prompt_options)})
|
181
|
+
|
182
|
+
assert options.project_id is not None
|
183
|
+
|
184
|
+
async with self._client.request(
|
185
|
+
handler=RequestHandler.GetOrCreatePrompt,
|
186
|
+
params=GetOrCreatePromptRequestParams(
|
187
|
+
project_id=options.project_id,
|
188
|
+
version_uuid=options.version_uuid,
|
189
|
+
),
|
190
|
+
body=GetOrCreatePromptRequestBody(
|
191
|
+
path=path,
|
192
|
+
prompt=options.prompt,
|
193
|
+
),
|
194
|
+
) as response:
|
195
|
+
return GetOrCreatePromptResult.model_validate_json(response.content)
|
196
|
+
|
197
|
+
async def run(self, path: str, options: RunPromptOptions) -> Optional[RunPromptResult]:
|
198
|
+
try:
|
199
|
+
prompt_options = self._ensure_options(options)
|
200
|
+
options = RunPromptOptions(**{**dict(options), **dict(prompt_options)})
|
201
|
+
|
202
|
+
assert options.project_id is not None
|
203
|
+
|
204
|
+
async with self._client.request(
|
205
|
+
handler=RequestHandler.RunPrompt,
|
206
|
+
params=RunPromptRequestParams(
|
207
|
+
project_id=options.project_id,
|
208
|
+
version_uuid=options.version_uuid,
|
209
|
+
),
|
210
|
+
body=RunPromptRequestBody(
|
211
|
+
path=path,
|
212
|
+
parameters=options.parameters,
|
213
|
+
custom_identifier=options.custom_identifier,
|
214
|
+
stream=options.stream,
|
215
|
+
),
|
216
|
+
) as response:
|
217
|
+
if options.stream:
|
218
|
+
result = await self._handle_stream(
|
219
|
+
response.sse(),
|
220
|
+
callbacks=StreamCallbacks(
|
221
|
+
on_event=options.on_event,
|
222
|
+
on_finished=options.on_finished,
|
223
|
+
on_error=options.on_error,
|
224
|
+
),
|
225
|
+
)
|
226
|
+
else:
|
227
|
+
result = RunPromptResult.model_validate_json(response.content)
|
228
|
+
|
229
|
+
if options.on_finished:
|
230
|
+
options.on_finished(FinishedEvent(**dict(result)))
|
231
|
+
|
232
|
+
return RunPromptResult(**dict(result))
|
233
|
+
|
234
|
+
except Exception as exception:
|
235
|
+
if not isinstance(exception, ApiError):
|
236
|
+
exception = ApiError(
|
237
|
+
status=500,
|
238
|
+
code=ApiErrorCodes.InternalServerError,
|
239
|
+
message=str(exception),
|
240
|
+
response=str(exception),
|
241
|
+
)
|
242
|
+
|
243
|
+
if not options.on_error:
|
244
|
+
raise exception
|
245
|
+
|
246
|
+
options.on_error(exception)
|
247
|
+
|
248
|
+
return None
|
249
|
+
|
250
|
+
async def chat(self, uuid: str, messages: List[Message], options: ChatPromptOptions) -> Optional[ChatPromptResult]:
|
251
|
+
try:
|
252
|
+
async with self._client.request(
|
253
|
+
handler=RequestHandler.ChatPrompt,
|
254
|
+
params=ChatPromptRequestParams(
|
255
|
+
conversation_uuid=uuid,
|
256
|
+
),
|
257
|
+
body=ChatPromptRequestBody(
|
258
|
+
messages=messages,
|
259
|
+
stream=options.stream,
|
260
|
+
),
|
261
|
+
) as response:
|
262
|
+
if options.stream:
|
263
|
+
result = await self._handle_stream(
|
264
|
+
response.sse(),
|
265
|
+
callbacks=StreamCallbacks(
|
266
|
+
on_event=options.on_event,
|
267
|
+
on_finished=options.on_finished,
|
268
|
+
on_error=options.on_error,
|
269
|
+
),
|
270
|
+
)
|
271
|
+
else:
|
272
|
+
result = ChatPromptResult.model_validate_json(response.content)
|
273
|
+
|
274
|
+
if options.on_finished:
|
275
|
+
options.on_finished(FinishedEvent(**dict(result)))
|
276
|
+
|
277
|
+
return ChatPromptResult(**dict(result))
|
278
|
+
|
279
|
+
except Exception as exception:
|
280
|
+
if not isinstance(exception, ApiError):
|
281
|
+
exception = ApiError(
|
282
|
+
status=500,
|
283
|
+
code=ApiErrorCodes.InternalServerError,
|
284
|
+
message=str(exception),
|
285
|
+
response=str(exception),
|
286
|
+
)
|
287
|
+
|
288
|
+
if not options.on_error:
|
289
|
+
raise exception
|
290
|
+
|
291
|
+
options.on_error(exception)
|
292
|
+
|
293
|
+
return None
|
294
|
+
|
295
|
+
# TODO: render - needs PromptL in Python
|
296
|
+
|
297
|
+
# TODO: render_chain - needs PromptL in Python
|
@@ -0,0 +1,296 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from typing import Any, Dict, List, Optional, Protocol, Union, runtime_checkable
|
3
|
+
|
4
|
+
from latitude_sdk.sdk.errors import ApiError
|
5
|
+
from latitude_sdk.util import Field, Model, StrEnum
|
6
|
+
|
7
|
+
|
8
|
+
class DbErrorRef(Model):
|
9
|
+
entity_uuid: str = Field(alias=str("entityUuid"))
|
10
|
+
entity_type: str = Field(alias=str("entityType"))
|
11
|
+
|
12
|
+
|
13
|
+
class Providers(StrEnum):
|
14
|
+
OpenAI = "openai"
|
15
|
+
Anthropic = "anthropic"
|
16
|
+
Groq = "groq"
|
17
|
+
Mistral = "mistral"
|
18
|
+
Azure = "azure"
|
19
|
+
Google = "google"
|
20
|
+
Custom = "custom"
|
21
|
+
|
22
|
+
|
23
|
+
class Prompt(Model):
|
24
|
+
uuid: str
|
25
|
+
path: str
|
26
|
+
content: str
|
27
|
+
config: Dict[str, Any]
|
28
|
+
provider: Optional[Providers] = None
|
29
|
+
|
30
|
+
|
31
|
+
class ContentType(StrEnum):
|
32
|
+
Text = "text"
|
33
|
+
Image = "image"
|
34
|
+
File = "file"
|
35
|
+
ToolCall = "tool-call"
|
36
|
+
ToolResult = "tool-result"
|
37
|
+
|
38
|
+
|
39
|
+
class TextContent(Model):
|
40
|
+
type: ContentType = ContentType.Text
|
41
|
+
text: str
|
42
|
+
|
43
|
+
|
44
|
+
class ImageContent(Model):
|
45
|
+
type: ContentType = ContentType.Image
|
46
|
+
image: str
|
47
|
+
|
48
|
+
|
49
|
+
class FileContent(Model):
|
50
|
+
type: ContentType = ContentType.File
|
51
|
+
file: str
|
52
|
+
mime_type: str = Field(alias=str("mimeType"))
|
53
|
+
|
54
|
+
|
55
|
+
class ToolCallContent(Model):
|
56
|
+
type: ContentType = ContentType.ToolCall
|
57
|
+
tool_call_id: str = Field(alias=str("toolCallId"))
|
58
|
+
tool_name: str = Field(alias=str("toolName"))
|
59
|
+
args: Dict[str, Any]
|
60
|
+
|
61
|
+
|
62
|
+
class ToolResultContent(Model):
|
63
|
+
type: ContentType = ContentType.ToolResult
|
64
|
+
tool_call_id: str = Field(alias=str("toolCallId"))
|
65
|
+
tool_name: str = Field(alias=str("toolName"))
|
66
|
+
result: str
|
67
|
+
is_error: Optional[bool] = Field(None, alias=str("isError"))
|
68
|
+
|
69
|
+
|
70
|
+
MessageContent = Union[
|
71
|
+
str,
|
72
|
+
List[TextContent],
|
73
|
+
List[ImageContent],
|
74
|
+
List[FileContent],
|
75
|
+
List[ToolCallContent],
|
76
|
+
List[ToolResultContent],
|
77
|
+
]
|
78
|
+
|
79
|
+
|
80
|
+
class MessageRole(StrEnum):
|
81
|
+
System = "system"
|
82
|
+
User = "user"
|
83
|
+
Assistant = "assistant"
|
84
|
+
Tool = "tool"
|
85
|
+
|
86
|
+
|
87
|
+
class SystemMessage(Model):
|
88
|
+
role: MessageRole = MessageRole.System
|
89
|
+
content: Union[str, List[TextContent]]
|
90
|
+
|
91
|
+
|
92
|
+
class UserMessage(Model):
|
93
|
+
role: MessageRole = MessageRole.User
|
94
|
+
content: Union[str, List[TextContent], List[ImageContent], List[FileContent]]
|
95
|
+
name: Optional[str] = None
|
96
|
+
|
97
|
+
|
98
|
+
class ToolCall(Model):
|
99
|
+
id: str
|
100
|
+
name: str
|
101
|
+
arguments: Dict[str, Any]
|
102
|
+
|
103
|
+
|
104
|
+
class AssistantMessage(Model):
|
105
|
+
role: MessageRole = MessageRole.Assistant
|
106
|
+
content: Union[str, List[TextContent], List[ToolCallContent]]
|
107
|
+
tool_calls: Optional[List[ToolCall]] = Field(None, alias=str("toolCalls"))
|
108
|
+
|
109
|
+
|
110
|
+
class ToolMessage(Model):
|
111
|
+
role: MessageRole = MessageRole.Tool
|
112
|
+
content: List[ToolResultContent]
|
113
|
+
|
114
|
+
|
115
|
+
Message = Union[SystemMessage, UserMessage, AssistantMessage, ToolMessage]
|
116
|
+
|
117
|
+
|
118
|
+
class ModelUsage(Model):
|
119
|
+
prompt_tokens: int = Field(alias=str("promptTokens"))
|
120
|
+
completion_tokens: int = Field(alias=str("completionTokens"))
|
121
|
+
total_tokens: int = Field(alias=str("totalTokens"))
|
122
|
+
|
123
|
+
|
124
|
+
class StreamTypes(StrEnum):
|
125
|
+
Text = "text"
|
126
|
+
Object = "object"
|
127
|
+
|
128
|
+
|
129
|
+
class ChainTextResponse(Model):
|
130
|
+
type: StreamTypes = StreamTypes.Text
|
131
|
+
text: str
|
132
|
+
tool_calls: Optional[List[ToolCall]] = Field(None, alias=str("toolCalls"))
|
133
|
+
usage: ModelUsage
|
134
|
+
|
135
|
+
|
136
|
+
class ChainObjectResponse(Model):
|
137
|
+
type: StreamTypes = StreamTypes.Object
|
138
|
+
object: Any
|
139
|
+
usage: ModelUsage
|
140
|
+
|
141
|
+
|
142
|
+
ChainResponse = Union[ChainTextResponse, ChainObjectResponse]
|
143
|
+
|
144
|
+
|
145
|
+
class ChainError(Model):
|
146
|
+
name: str
|
147
|
+
message: str
|
148
|
+
stack: Optional[str] = None
|
149
|
+
|
150
|
+
|
151
|
+
class StreamEvents(StrEnum):
|
152
|
+
Latitude = "latitude-event"
|
153
|
+
Provider = "provider-event"
|
154
|
+
Finished = "finished-event"
|
155
|
+
|
156
|
+
|
157
|
+
ProviderEvent = Dict[str, Any]
|
158
|
+
|
159
|
+
|
160
|
+
class ChainEvents(StrEnum):
|
161
|
+
Step = "chain-step"
|
162
|
+
StepCompleted = "chain-step-complete"
|
163
|
+
Completed = "chain-complete"
|
164
|
+
Error = "chain-error"
|
165
|
+
|
166
|
+
|
167
|
+
class ChainEventStep(Model):
|
168
|
+
event: StreamEvents = StreamEvents.Latitude
|
169
|
+
type: ChainEvents = ChainEvents.Step
|
170
|
+
config: Dict[str, Any]
|
171
|
+
is_last_step: bool = Field(alias=str("isLastStep"))
|
172
|
+
messages: List[Message]
|
173
|
+
uuid: Optional[str] = None
|
174
|
+
|
175
|
+
|
176
|
+
class ChainEventStepCompleted(Model):
|
177
|
+
event: StreamEvents = StreamEvents.Latitude
|
178
|
+
type: ChainEvents = ChainEvents.StepCompleted
|
179
|
+
response: ChainResponse
|
180
|
+
uuid: Optional[str] = None
|
181
|
+
|
182
|
+
|
183
|
+
class ChainEventCompleted(Model):
|
184
|
+
event: StreamEvents = StreamEvents.Latitude
|
185
|
+
type: ChainEvents = ChainEvents.Completed
|
186
|
+
config: Dict[str, Any]
|
187
|
+
messages: Optional[List[Message]] = None
|
188
|
+
object: Optional[Any] = None
|
189
|
+
response: ChainResponse
|
190
|
+
uuid: Optional[str] = None
|
191
|
+
|
192
|
+
|
193
|
+
class ChainEventError(Model):
|
194
|
+
event: StreamEvents = StreamEvents.Latitude
|
195
|
+
type: ChainEvents = ChainEvents.Error
|
196
|
+
error: ChainError
|
197
|
+
|
198
|
+
|
199
|
+
ChainEvent = Union[ChainEventStep, ChainEventStepCompleted, ChainEventCompleted, ChainEventError]
|
200
|
+
|
201
|
+
|
202
|
+
LatitudeEvent = ChainEvent
|
203
|
+
|
204
|
+
|
205
|
+
class FinishedEvent(Model):
|
206
|
+
event: StreamEvents = StreamEvents.Finished
|
207
|
+
uuid: str
|
208
|
+
conversation: List[Message]
|
209
|
+
response: ChainResponse
|
210
|
+
|
211
|
+
|
212
|
+
StreamEvent = Union[ProviderEvent, LatitudeEvent, FinishedEvent]
|
213
|
+
|
214
|
+
|
215
|
+
class LogSources(StrEnum):
|
216
|
+
Api = "api"
|
217
|
+
Playground = "playground"
|
218
|
+
Evaluation = "evaluation"
|
219
|
+
User = "user"
|
220
|
+
SharedPrompt = "shared_prompt"
|
221
|
+
|
222
|
+
|
223
|
+
class Log(Model):
|
224
|
+
id: int
|
225
|
+
uuid: str
|
226
|
+
source: Optional[LogSources] = None
|
227
|
+
commit_id: int = Field(alias=str("commitId"))
|
228
|
+
resolved_content: str = Field(alias=str("resolvedContent"))
|
229
|
+
content_hash: str = Field(alias=str("contentHash"))
|
230
|
+
parameters: Dict[str, Any]
|
231
|
+
custom_identifier: Optional[str] = Field(None, alias=str("customIdentifier"))
|
232
|
+
duration: Optional[int] = None
|
233
|
+
created_at: datetime = Field(alias=str("createdAt"))
|
234
|
+
updated_at: datetime = Field(alias=str("updatedAt"))
|
235
|
+
|
236
|
+
|
237
|
+
class EvaluationResultType(StrEnum):
|
238
|
+
Boolean = "evaluation_resultable_booleans"
|
239
|
+
Text = "evaluation_resultable_texts"
|
240
|
+
Number = "evaluation_resultable_numbers"
|
241
|
+
|
242
|
+
|
243
|
+
class EvaluationResult(Model):
|
244
|
+
id: int
|
245
|
+
uuid: str
|
246
|
+
evaluation_id: int = Field(alias=str("evaluationId"))
|
247
|
+
document_log_id: int = Field(alias=str("documentLogId"))
|
248
|
+
evaluated_provider_log_id: Optional[int] = Field(None, alias=str("evaluatedProviderLogId"))
|
249
|
+
evaluation_provider_log_id: Optional[int] = Field(None, alias=str("evaluationProviderLogId"))
|
250
|
+
resultable_type: Optional[EvaluationResultType] = Field(None, alias=str("resultableType"))
|
251
|
+
resultable_id: Optional[int] = Field(None, alias=str("resultableId"))
|
252
|
+
result: Optional[Union[str, bool, int]] = None
|
253
|
+
source: Optional[LogSources] = None
|
254
|
+
reason: Optional[str] = None
|
255
|
+
created_at: datetime = Field(alias=str("createdAt"))
|
256
|
+
updated_at: datetime = Field(alias=str("updatedAt"))
|
257
|
+
|
258
|
+
|
259
|
+
class StreamCallbacks(Model):
|
260
|
+
@runtime_checkable
|
261
|
+
class OnEvent(Protocol):
|
262
|
+
def __call__(self, event: StreamEvent): ...
|
263
|
+
|
264
|
+
on_event: Optional[OnEvent] = None
|
265
|
+
|
266
|
+
@runtime_checkable
|
267
|
+
class OnFinished(Protocol):
|
268
|
+
def __call__(self, event: FinishedEvent): ...
|
269
|
+
|
270
|
+
on_finished: Optional[OnFinished] = None
|
271
|
+
|
272
|
+
@runtime_checkable
|
273
|
+
class OnError(Protocol):
|
274
|
+
def __call__(self, error: ApiError): ...
|
275
|
+
|
276
|
+
on_error: Optional[OnError] = None
|
277
|
+
|
278
|
+
|
279
|
+
class SdkOptions(Model):
|
280
|
+
project_id: Optional[int] = None
|
281
|
+
version_uuid: Optional[str] = None
|
282
|
+
|
283
|
+
|
284
|
+
class GatewayOptions(Model):
|
285
|
+
host: str
|
286
|
+
port: int
|
287
|
+
ssl: bool
|
288
|
+
api_version: str
|
289
|
+
|
290
|
+
@property
|
291
|
+
def protocol(self) -> str:
|
292
|
+
return "https" if self.ssl else "http"
|
293
|
+
|
294
|
+
@property
|
295
|
+
def base_url(self) -> str:
|
296
|
+
return f"{self.protocol}://{self.host}:{self.port}/api/{self.api_version}"
|
@@ -0,0 +1 @@
|
|
1
|
+
from .utils import *
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import os
|
2
|
+
from enum import Enum
|
3
|
+
from typing import Any, Callable, List, TypeVar
|
4
|
+
|
5
|
+
import pydantic
|
6
|
+
from typing_extensions import ParamSpec
|
7
|
+
|
8
|
+
T = TypeVar("T", str, bool, int, List[str])
|
9
|
+
|
10
|
+
|
11
|
+
def get_env(key: str, default: T) -> T:
|
12
|
+
value = os.getenv(key)
|
13
|
+
if not value:
|
14
|
+
return default
|
15
|
+
|
16
|
+
if isinstance(default, str):
|
17
|
+
return value
|
18
|
+
|
19
|
+
elif isinstance(default, bool):
|
20
|
+
return value.lower() in ["true", "1", "yes", "on"]
|
21
|
+
|
22
|
+
elif isinstance(default, int):
|
23
|
+
return int(value)
|
24
|
+
|
25
|
+
elif isinstance(default, list):
|
26
|
+
return value.split(",")
|
27
|
+
|
28
|
+
raise TypeError(f"Unknown type {type(default)}")
|
29
|
+
|
30
|
+
|
31
|
+
P = ParamSpec("P")
|
32
|
+
R = TypeVar("R")
|
33
|
+
|
34
|
+
|
35
|
+
def returns_identity(x: R) -> R:
|
36
|
+
return x
|
37
|
+
|
38
|
+
|
39
|
+
def is_like(func: Callable[P, R]) -> Callable[[Callable[..., Any]], Callable[P, R]]:
|
40
|
+
return returns_identity # type: ignore
|
41
|
+
|
42
|
+
|
43
|
+
def returns_like(func: Callable[..., R]) -> Callable[[Callable[P, Any]], Callable[P, R]]:
|
44
|
+
return returns_identity # type: ignore
|
45
|
+
|
46
|
+
|
47
|
+
class StrEnum(str, Enum):
|
48
|
+
def __str__(self) -> str:
|
49
|
+
return str(self.value)
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def list(cls) -> List[str]:
|
53
|
+
return [v.value for v in cls]
|
54
|
+
|
55
|
+
|
56
|
+
Field = pydantic.Field
|
57
|
+
Config = pydantic.ConfigDict
|
58
|
+
|
59
|
+
|
60
|
+
class Model(pydantic.BaseModel):
|
61
|
+
model_config = Config(populate_by_name=True, arbitrary_types_allowed=True, strict=True)
|
62
|
+
|
63
|
+
@is_like(pydantic.BaseModel.__iter__)
|
64
|
+
def __iter__(self) -> Any:
|
65
|
+
yield from [(k, v) for k, v in super().__iter__() if v is not None]
|
66
|
+
|
67
|
+
@is_like(pydantic.BaseModel.model_dump)
|
68
|
+
def model_dump(self, *args: Any, **kwargs: Any) -> Any:
|
69
|
+
exclude_none = kwargs.pop("exclude_none", True)
|
70
|
+
return super().model_dump(*args, exclude_none=exclude_none, **kwargs)
|
71
|
+
|
72
|
+
@is_like(pydantic.BaseModel.dict) # pyright: ignore [reportDeprecated]
|
73
|
+
def dict(self, *args: Any, **kwargs: Any) -> Any:
|
74
|
+
exclude_none = kwargs.pop("exclude_none", True)
|
75
|
+
return super().dict(*args, exclude_none=exclude_none, **kwargs) # pyright: ignore [reportDeprecated]
|
76
|
+
|
77
|
+
@is_like(pydantic.BaseModel.model_dump_json)
|
78
|
+
def model_dump_json(self, *args: Any, **kwargs: Any) -> Any:
|
79
|
+
exclude_none = kwargs.pop("exclude_none", True)
|
80
|
+
by_alias = kwargs.pop("by_alias", True)
|
81
|
+
return super().model_dump_json(*args, exclude_none=exclude_none, by_alias=by_alias, **kwargs)
|
82
|
+
|
83
|
+
@is_like(pydantic.BaseModel.json) # pyright: ignore [reportDeprecated]
|
84
|
+
def json(self, *args: Any, **kwargs: Any) -> Any:
|
85
|
+
exclude_none = kwargs.pop("exclude_none", True)
|
86
|
+
by_alias = kwargs.pop("by_alias", True)
|
87
|
+
return super().json(*args, exclude_none=exclude_none, by_alias=by_alias, **kwargs) # pyright: ignore [reportDeprecated]
|