latitude-sdk 0.1.0b2__tar.gz → 0.1.0b4__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.
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/PKG-INFO +1 -1
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/pyproject.toml +2 -1
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/client/client.py +1 -1
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/client/payloads.py +3 -3
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/env/env.py +1 -1
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/sdk/errors.py +10 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/sdk/logs.py +3 -3
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/sdk/prompts.py +8 -6
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/sdk/types.py +34 -34
- latitude_sdk-0.1.0b4/tests/__init__.py +0 -0
- latitude_sdk-0.1.0b4/tests/evaluations/__init__.py +0 -0
- latitude_sdk-0.1.0b4/tests/evaluations/create_result_test.py +61 -0
- latitude_sdk-0.1.0b4/tests/evaluations/trigger_test.py +56 -0
- latitude_sdk-0.1.0b4/tests/logs/__init__.py +0 -0
- latitude_sdk-0.1.0b4/tests/logs/create_test.py +117 -0
- latitude_sdk-0.1.0b4/tests/prompts/__init__.py +0 -0
- latitude_sdk-0.1.0b4/tests/prompts/chat_test.py +302 -0
- latitude_sdk-0.1.0b4/tests/prompts/get_or_create_test.py +108 -0
- latitude_sdk-0.1.0b4/tests/prompts/get_test.py +69 -0
- latitude_sdk-0.1.0b4/tests/prompts/run_test.py +491 -0
- latitude_sdk-0.1.0b4/tests/utils/__init__.py +2 -0
- latitude_sdk-0.1.0b4/tests/utils/fixtures.py +497 -0
- latitude_sdk-0.1.0b4/tests/utils/utils.py +132 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/uv.lock +15 -1
- latitude_sdk-0.1.0b2/tests/prompts/get_test.py +0 -32
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/.gitignore +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/.python-version +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/README.md +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/scripts/format.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/scripts/lint.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/scripts/test.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/__init__.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/client/__init__.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/client/router.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/env/__init__.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/py.typed +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/sdk/__init__.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/sdk/evaluations.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/sdk/latitude.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/util/__init__.py +0 -0
- {latitude_sdk-0.1.0b2 → latitude_sdk-0.1.0b4}/src/latitude_sdk/util/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: latitude-sdk
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.0b4
|
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
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "latitude-sdk"
|
3
|
-
version = "0.1.0-beta.
|
3
|
+
version = "0.1.0-beta.4"
|
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" }]
|
@@ -22,6 +22,7 @@ dev = [
|
|
22
22
|
"pytest-asyncio>=0.24.0",
|
23
23
|
"pytest-xdist>=3.6.1",
|
24
24
|
"pytest>=8.3.4",
|
25
|
+
"respx>=0.22.0",
|
25
26
|
"pyright>=1.1.391",
|
26
27
|
"ruff>=0.8.3",
|
27
28
|
"sh>=1.14.3",
|
@@ -16,7 +16,7 @@ from latitude_sdk.sdk.errors import (
|
|
16
16
|
from latitude_sdk.sdk.types import LogSources
|
17
17
|
from latitude_sdk.util import Model
|
18
18
|
|
19
|
-
RETRIABLE_STATUSES = [429, 500, 502, 503, 504]
|
19
|
+
RETRIABLE_STATUSES = [408, 409, 429, 500, 502, 503, 504]
|
20
20
|
|
21
21
|
ClientEvent = httpx_sse.ServerSentEvent
|
22
22
|
|
@@ -9,7 +9,7 @@ class ErrorResponse(Model):
|
|
9
9
|
code: str = Field(alias=str("errorCode"))
|
10
10
|
message: str
|
11
11
|
details: Dict[str, Any]
|
12
|
-
db_ref: Optional[DbErrorRef] = Field(None, alias=str("dbErrorRef"))
|
12
|
+
db_ref: Optional[DbErrorRef] = Field(default=None, alias=str("dbErrorRef"))
|
13
13
|
|
14
14
|
|
15
15
|
class PromptRequestParams(Model):
|
@@ -37,7 +37,7 @@ class RunPromptRequestParams(PromptRequestParams, Model):
|
|
37
37
|
class RunPromptRequestBody(Model):
|
38
38
|
path: str
|
39
39
|
parameters: Optional[Dict[str, Any]] = None
|
40
|
-
custom_identifier: Optional[str] = Field(None, alias=str("customIdentifier"))
|
40
|
+
custom_identifier: Optional[str] = Field(default=None, alias=str("customIdentifier"))
|
41
41
|
stream: Optional[bool] = None
|
42
42
|
|
43
43
|
|
@@ -74,7 +74,7 @@ class TriggerEvaluationRequestParams(EvaluationRequestParams, Model):
|
|
74
74
|
|
75
75
|
|
76
76
|
class TriggerEvaluationRequestBody(Model):
|
77
|
-
evaluation_uuids: Optional[List[str]] = Field(None, alias=str("evaluationUuids"))
|
77
|
+
evaluation_uuids: Optional[List[str]] = Field(default=None, alias=str("evaluationUuids"))
|
78
78
|
|
79
79
|
|
80
80
|
class CreateEvaluationResultRequestParams(EvaluationRequestParams, Model):
|
@@ -57,3 +57,13 @@ class ApiError(Exception):
|
|
57
57
|
return f"Unexpected API Error: {status} {message}"
|
58
58
|
|
59
59
|
return message
|
60
|
+
|
61
|
+
def __eq__(self, other: object) -> bool:
|
62
|
+
return (
|
63
|
+
isinstance(other, ApiError)
|
64
|
+
and self.status == other.status
|
65
|
+
and self.code == other.code
|
66
|
+
and self.message == other.message
|
67
|
+
and self.response == other.response
|
68
|
+
and self.db_ref == other.db_ref
|
69
|
+
)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import Iterable, Optional
|
2
2
|
|
3
3
|
from latitude_sdk.client import Client, CreateLogRequestBody, CreateLogRequestParams, RequestHandler
|
4
4
|
from latitude_sdk.sdk.errors import ApiError, ApiErrorCodes
|
@@ -45,7 +45,7 @@ class Logs:
|
|
45
45
|
|
46
46
|
return LogOptions(project_id=project_id, version_uuid=version_uuid)
|
47
47
|
|
48
|
-
async def create(self, path: str, messages:
|
48
|
+
async def create(self, path: str, messages: Iterable[Message], options: CreateLogOptions) -> CreateLogResult:
|
49
49
|
log_options = self._ensure_options(options)
|
50
50
|
options = CreateLogOptions(**{**dict(options), **dict(log_options)})
|
51
51
|
|
@@ -59,7 +59,7 @@ class Logs:
|
|
59
59
|
),
|
60
60
|
body=CreateLogRequestBody(
|
61
61
|
path=path,
|
62
|
-
messages=messages,
|
62
|
+
messages=list(messages),
|
63
63
|
response=options.response,
|
64
64
|
),
|
65
65
|
) as response:
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Any, AsyncGenerator, Dict, List, Optional
|
1
|
+
from typing import Any, AsyncGenerator, Dict, Iterable, List, Optional
|
2
2
|
|
3
3
|
from latitude_sdk.client import (
|
4
4
|
ChatPromptRequestBody,
|
@@ -119,7 +119,7 @@ class Prompts:
|
|
119
119
|
elif type == str(ChainEvents.Error):
|
120
120
|
event = ChainEventError.model_validate_json(stream_event.data)
|
121
121
|
raise ApiError(
|
122
|
-
status=
|
122
|
+
status=400,
|
123
123
|
code=ApiErrorCodes.AIRunError,
|
124
124
|
message=event.error.message,
|
125
125
|
response=stream_event.data,
|
@@ -130,7 +130,7 @@ class Prompts:
|
|
130
130
|
status=500,
|
131
131
|
code=ApiErrorCodes.InternalServerError,
|
132
132
|
message=f"Unknown latitude event: {type}",
|
133
|
-
response=
|
133
|
+
response=stream_event.data,
|
134
134
|
)
|
135
135
|
|
136
136
|
elif stream_event.event == str(StreamEvents.Provider):
|
@@ -142,7 +142,7 @@ class Prompts:
|
|
142
142
|
status=500,
|
143
143
|
code=ApiErrorCodes.InternalServerError,
|
144
144
|
message=f"Unknown stream event: {stream_event.event}",
|
145
|
-
response=
|
145
|
+
response=stream_event.data,
|
146
146
|
)
|
147
147
|
|
148
148
|
if callbacks.on_event:
|
@@ -247,7 +247,9 @@ class Prompts:
|
|
247
247
|
|
248
248
|
return None
|
249
249
|
|
250
|
-
async def chat(
|
250
|
+
async def chat(
|
251
|
+
self, uuid: str, messages: Iterable[Message], options: ChatPromptOptions
|
252
|
+
) -> Optional[ChatPromptResult]:
|
251
253
|
try:
|
252
254
|
async with self._client.request(
|
253
255
|
handler=RequestHandler.ChatPrompt,
|
@@ -255,7 +257,7 @@ class Prompts:
|
|
255
257
|
conversation_uuid=uuid,
|
256
258
|
),
|
257
259
|
body=ChatPromptRequestBody(
|
258
|
-
messages=messages,
|
260
|
+
messages=list(messages),
|
259
261
|
stream=options.stream,
|
260
262
|
),
|
261
263
|
) as response:
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from datetime import datetime
|
2
|
-
from typing import Any, Dict, List, Optional, Protocol, Union, runtime_checkable
|
2
|
+
from typing import Any, Dict, List, Literal, Optional, Protocol, Union, runtime_checkable
|
3
3
|
|
4
4
|
from latitude_sdk.sdk.errors import ApiError
|
5
5
|
from latitude_sdk.util import Field, Model, StrEnum
|
@@ -37,34 +37,34 @@ class ContentType(StrEnum):
|
|
37
37
|
|
38
38
|
|
39
39
|
class TextContent(Model):
|
40
|
-
type: ContentType = ContentType.Text
|
40
|
+
type: Literal[ContentType.Text] = ContentType.Text
|
41
41
|
text: str
|
42
42
|
|
43
43
|
|
44
44
|
class ImageContent(Model):
|
45
|
-
type: ContentType = ContentType.Image
|
45
|
+
type: Literal[ContentType.Image] = ContentType.Image
|
46
46
|
image: str
|
47
47
|
|
48
48
|
|
49
49
|
class FileContent(Model):
|
50
|
-
type: ContentType = ContentType.File
|
50
|
+
type: Literal[ContentType.File] = ContentType.File
|
51
51
|
file: str
|
52
52
|
mime_type: str = Field(alias=str("mimeType"))
|
53
53
|
|
54
54
|
|
55
55
|
class ToolCallContent(Model):
|
56
|
-
type: ContentType = ContentType.ToolCall
|
56
|
+
type: Literal[ContentType.ToolCall] = ContentType.ToolCall
|
57
57
|
tool_call_id: str = Field(alias=str("toolCallId"))
|
58
58
|
tool_name: str = Field(alias=str("toolName"))
|
59
|
-
|
59
|
+
tool_arguments: Dict[str, Any] = Field(alias=str("toolArguments"))
|
60
60
|
|
61
61
|
|
62
62
|
class ToolResultContent(Model):
|
63
|
-
type: ContentType = ContentType.ToolResult
|
63
|
+
type: Literal[ContentType.ToolResult] = ContentType.ToolResult
|
64
64
|
tool_call_id: str = Field(alias=str("toolCallId"))
|
65
65
|
tool_name: str = Field(alias=str("toolName"))
|
66
66
|
result: str
|
67
|
-
is_error: Optional[bool] = Field(None, alias=str("isError"))
|
67
|
+
is_error: Optional[bool] = Field(default=None, alias=str("isError"))
|
68
68
|
|
69
69
|
|
70
70
|
MessageContent = Union[
|
@@ -85,12 +85,12 @@ class MessageRole(StrEnum):
|
|
85
85
|
|
86
86
|
|
87
87
|
class SystemMessage(Model):
|
88
|
-
role: MessageRole = MessageRole.System
|
88
|
+
role: Literal[MessageRole.System] = MessageRole.System
|
89
89
|
content: Union[str, List[TextContent]]
|
90
90
|
|
91
91
|
|
92
92
|
class UserMessage(Model):
|
93
|
-
role: MessageRole = MessageRole.User
|
93
|
+
role: Literal[MessageRole.User] = MessageRole.User
|
94
94
|
content: Union[str, List[Union[TextContent, ImageContent, FileContent]]]
|
95
95
|
name: Optional[str] = None
|
96
96
|
|
@@ -102,13 +102,13 @@ class ToolCall(Model):
|
|
102
102
|
|
103
103
|
|
104
104
|
class AssistantMessage(Model):
|
105
|
-
role: MessageRole = MessageRole.Assistant
|
105
|
+
role: Literal[MessageRole.Assistant] = MessageRole.Assistant
|
106
106
|
content: Union[str, List[Union[TextContent, ToolCallContent]]]
|
107
|
-
tool_calls: Optional[List[ToolCall]] = Field(None, alias=str("toolCalls"))
|
107
|
+
tool_calls: Optional[List[ToolCall]] = Field(default=None, alias=str("toolCalls"))
|
108
108
|
|
109
109
|
|
110
110
|
class ToolMessage(Model):
|
111
|
-
role: MessageRole = MessageRole.Tool
|
111
|
+
role: Literal[MessageRole.Tool] = MessageRole.Tool
|
112
112
|
content: List[ToolResultContent]
|
113
113
|
|
114
114
|
|
@@ -127,14 +127,14 @@ class StreamTypes(StrEnum):
|
|
127
127
|
|
128
128
|
|
129
129
|
class ChainTextResponse(Model):
|
130
|
-
type: StreamTypes = StreamTypes.Text
|
130
|
+
type: Literal[StreamTypes.Text] = Field(default=StreamTypes.Text, alias=str("streamType"))
|
131
131
|
text: str
|
132
|
-
tool_calls: Optional[List[ToolCall]] = Field(None, alias=str("toolCalls"))
|
132
|
+
tool_calls: Optional[List[ToolCall]] = Field(default=None, alias=str("toolCalls"))
|
133
133
|
usage: ModelUsage
|
134
134
|
|
135
135
|
|
136
136
|
class ChainObjectResponse(Model):
|
137
|
-
type: StreamTypes = StreamTypes.Object
|
137
|
+
type: Literal[StreamTypes.Object] = Field(default=StreamTypes.Object, alias=str("streamType"))
|
138
138
|
object: Any
|
139
139
|
usage: ModelUsage
|
140
140
|
|
@@ -165,34 +165,34 @@ class ChainEvents(StrEnum):
|
|
165
165
|
|
166
166
|
|
167
167
|
class ChainEventStep(Model):
|
168
|
-
event: StreamEvents = StreamEvents.Latitude
|
169
|
-
type: ChainEvents = ChainEvents.Step
|
170
|
-
|
168
|
+
event: Literal[StreamEvents.Latitude] = StreamEvents.Latitude
|
169
|
+
type: Literal[ChainEvents.Step] = ChainEvents.Step
|
170
|
+
uuid: Optional[str] = None
|
171
171
|
is_last_step: bool = Field(alias=str("isLastStep"))
|
172
|
+
config: Dict[str, Any]
|
172
173
|
messages: List[Message]
|
173
|
-
uuid: Optional[str] = None
|
174
174
|
|
175
175
|
|
176
176
|
class ChainEventStepCompleted(Model):
|
177
|
-
event: StreamEvents = StreamEvents.Latitude
|
178
|
-
type: ChainEvents = ChainEvents.StepCompleted
|
179
|
-
response: ChainResponse
|
177
|
+
event: Literal[StreamEvents.Latitude] = StreamEvents.Latitude
|
178
|
+
type: Literal[ChainEvents.StepCompleted] = ChainEvents.StepCompleted
|
180
179
|
uuid: Optional[str] = None
|
180
|
+
response: ChainResponse
|
181
181
|
|
182
182
|
|
183
183
|
class ChainEventCompleted(Model):
|
184
|
-
event: StreamEvents = StreamEvents.Latitude
|
185
|
-
type: ChainEvents = ChainEvents.Completed
|
184
|
+
event: Literal[StreamEvents.Latitude] = StreamEvents.Latitude
|
185
|
+
type: Literal[ChainEvents.Completed] = ChainEvents.Completed
|
186
|
+
uuid: Optional[str] = None
|
186
187
|
config: Dict[str, Any]
|
187
188
|
messages: Optional[List[Message]] = None
|
188
189
|
object: Optional[Any] = None
|
189
190
|
response: ChainResponse
|
190
|
-
uuid: Optional[str] = None
|
191
191
|
|
192
192
|
|
193
193
|
class ChainEventError(Model):
|
194
|
-
event: StreamEvents = StreamEvents.Latitude
|
195
|
-
type: ChainEvents = ChainEvents.Error
|
194
|
+
event: Literal[StreamEvents.Latitude] = StreamEvents.Latitude
|
195
|
+
type: Literal[ChainEvents.Error] = ChainEvents.Error
|
196
196
|
error: ChainError
|
197
197
|
|
198
198
|
|
@@ -203,7 +203,7 @@ LatitudeEvent = ChainEvent
|
|
203
203
|
|
204
204
|
|
205
205
|
class FinishedEvent(Model):
|
206
|
-
event: StreamEvents = StreamEvents.Finished
|
206
|
+
event: Literal[StreamEvents.Finished] = StreamEvents.Finished
|
207
207
|
uuid: str
|
208
208
|
conversation: List[Message]
|
209
209
|
response: ChainResponse
|
@@ -228,7 +228,7 @@ class Log(Model):
|
|
228
228
|
resolved_content: str = Field(alias=str("resolvedContent"))
|
229
229
|
content_hash: str = Field(alias=str("contentHash"))
|
230
230
|
parameters: Dict[str, Any]
|
231
|
-
custom_identifier: Optional[str] = Field(None, alias=str("customIdentifier"))
|
231
|
+
custom_identifier: Optional[str] = Field(default=None, alias=str("customIdentifier"))
|
232
232
|
duration: Optional[int] = None
|
233
233
|
created_at: datetime = Field(alias=str("createdAt"))
|
234
234
|
updated_at: datetime = Field(alias=str("updatedAt"))
|
@@ -245,10 +245,10 @@ class EvaluationResult(Model):
|
|
245
245
|
uuid: str
|
246
246
|
evaluation_id: int = Field(alias=str("evaluationId"))
|
247
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"))
|
248
|
+
evaluated_provider_log_id: Optional[int] = Field(default=None, alias=str("evaluatedProviderLogId"))
|
249
|
+
evaluation_provider_log_id: Optional[int] = Field(default=None, alias=str("evaluationProviderLogId"))
|
250
|
+
resultable_type: Optional[EvaluationResultType] = Field(default=None, alias=str("resultableType"))
|
251
|
+
resultable_id: Optional[int] = Field(default=None, alias=str("resultableId"))
|
252
252
|
result: Optional[Union[str, bool, int]] = None
|
253
253
|
source: Optional[LogSources] = None
|
254
254
|
reason: Optional[str] = None
|
File without changes
|
File without changes
|
@@ -0,0 +1,61 @@
|
|
1
|
+
from typing import List, cast
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
|
5
|
+
from latitude_sdk import CreateEvaluationResultOptions, CreateEvaluationResultResult
|
6
|
+
from tests.utils import TestCase, fixtures
|
7
|
+
|
8
|
+
|
9
|
+
class TestCreateEvaluationResult(TestCase):
|
10
|
+
async def test_success(self):
|
11
|
+
conversation_uuid = "conversation-uuid"
|
12
|
+
evaluation_uuid = "evaluation-uuid"
|
13
|
+
options = CreateEvaluationResultOptions(result=True, reason="Because Yes")
|
14
|
+
endpoint = f"/conversations/{conversation_uuid}/evaluations/{evaluation_uuid}/evaluation-results"
|
15
|
+
endpoint_mock = self.gateway_mock.post(endpoint).mock(
|
16
|
+
return_value=httpx.Response(200, json=fixtures.EVALUATION_RESULT_RESPONSE)
|
17
|
+
)
|
18
|
+
|
19
|
+
result = await self.sdk.evaluations.create_result(conversation_uuid, evaluation_uuid, options)
|
20
|
+
request, _ = endpoint_mock.calls.last
|
21
|
+
|
22
|
+
self.assert_requested(
|
23
|
+
request,
|
24
|
+
method="POST",
|
25
|
+
endpoint=endpoint,
|
26
|
+
body={
|
27
|
+
"result": options.result,
|
28
|
+
"reason": options.reason,
|
29
|
+
"__internal": {"source": "api"},
|
30
|
+
},
|
31
|
+
)
|
32
|
+
self.assertEqual(endpoint_mock.call_count, 1)
|
33
|
+
self.assertEqual(result, CreateEvaluationResultResult(**dict(fixtures.EVALUATION_RESULT)))
|
34
|
+
|
35
|
+
async def test_fails(self):
|
36
|
+
conversation_uuid = "conversation-uuid"
|
37
|
+
evaluation_uuid = "evaluation-uuid"
|
38
|
+
options = CreateEvaluationResultOptions(result=True, reason="Because Yes")
|
39
|
+
endpoint = f"/conversations/{conversation_uuid}/evaluations/{evaluation_uuid}/evaluation-results"
|
40
|
+
endpoint_mock = self.gateway_mock.post(endpoint).mock(
|
41
|
+
return_value=httpx.Response(500, json=fixtures.ERROR_RESPONSE)
|
42
|
+
)
|
43
|
+
|
44
|
+
with self.assertRaisesRegex(type(fixtures.ERROR), fixtures.ERROR.message):
|
45
|
+
await self.sdk.evaluations.create_result(conversation_uuid, evaluation_uuid, options)
|
46
|
+
requests = cast(List[httpx.Request], [request for request, _ in endpoint_mock.calls]) # type: ignore
|
47
|
+
|
48
|
+
[
|
49
|
+
self.assert_requested(
|
50
|
+
request,
|
51
|
+
method="POST",
|
52
|
+
endpoint=endpoint,
|
53
|
+
body={
|
54
|
+
"result": options.result,
|
55
|
+
"reason": options.reason,
|
56
|
+
"__internal": {"source": "api"},
|
57
|
+
},
|
58
|
+
)
|
59
|
+
for request in requests
|
60
|
+
]
|
61
|
+
self.assertEqual(endpoint_mock.call_count, self.internal_options.retries)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
from typing import List, cast
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
|
5
|
+
from latitude_sdk import TriggerEvaluationOptions, TriggerEvaluationResult
|
6
|
+
from tests.utils import TestCase, fixtures
|
7
|
+
|
8
|
+
|
9
|
+
class TestTriggerEvaluation(TestCase):
|
10
|
+
async def test_success(self):
|
11
|
+
conversation_uuid = "conversation-uuid"
|
12
|
+
options = TriggerEvaluationOptions(evaluation_uuids=["evaluation-uuid-1", "evaluation-uuid-2"])
|
13
|
+
endpoint = f"/conversations/{conversation_uuid}/evaluate"
|
14
|
+
endpoint_mock = self.gateway_mock.post(endpoint).mock(
|
15
|
+
return_value=httpx.Response(200, json=fixtures.EVALUATIONS_RESPONSE)
|
16
|
+
)
|
17
|
+
|
18
|
+
result = await self.sdk.evaluations.trigger(conversation_uuid, options)
|
19
|
+
request, _ = endpoint_mock.calls.last
|
20
|
+
|
21
|
+
self.assert_requested(
|
22
|
+
request,
|
23
|
+
method="POST",
|
24
|
+
endpoint=endpoint,
|
25
|
+
body={
|
26
|
+
"evaluationUuids": options.evaluation_uuids,
|
27
|
+
"__internal": {"source": "api"},
|
28
|
+
},
|
29
|
+
)
|
30
|
+
self.assertEqual(endpoint_mock.call_count, 1)
|
31
|
+
self.assertEqual(result, TriggerEvaluationResult(evaluations=fixtures.EVALUATIONS))
|
32
|
+
|
33
|
+
async def test_fails(self):
|
34
|
+
conversation_uuid = "conversation-uuid"
|
35
|
+
options = TriggerEvaluationOptions()
|
36
|
+
endpoint = f"/conversations/{conversation_uuid}/evaluate"
|
37
|
+
endpoint_mock = self.gateway_mock.post(endpoint).mock(
|
38
|
+
return_value=httpx.Response(500, json=fixtures.ERROR_RESPONSE)
|
39
|
+
)
|
40
|
+
|
41
|
+
with self.assertRaisesRegex(type(fixtures.ERROR), fixtures.ERROR.message):
|
42
|
+
await self.sdk.evaluations.trigger(conversation_uuid, options)
|
43
|
+
requests = cast(List[httpx.Request], [request for request, _ in endpoint_mock.calls]) # type: ignore
|
44
|
+
|
45
|
+
[
|
46
|
+
self.assert_requested(
|
47
|
+
request,
|
48
|
+
method="POST",
|
49
|
+
endpoint=endpoint,
|
50
|
+
body={
|
51
|
+
"__internal": {"source": "api"},
|
52
|
+
},
|
53
|
+
)
|
54
|
+
for request in requests
|
55
|
+
]
|
56
|
+
self.assertEqual(endpoint_mock.call_count, self.internal_options.retries)
|
File without changes
|
@@ -0,0 +1,117 @@
|
|
1
|
+
import json
|
2
|
+
from typing import List, cast
|
3
|
+
|
4
|
+
import httpx
|
5
|
+
|
6
|
+
from latitude_sdk import CreateLogOptions, CreateLogResult
|
7
|
+
from tests.utils import TestCase, fixtures
|
8
|
+
|
9
|
+
|
10
|
+
class TestCreateLog(TestCase):
|
11
|
+
async def test_success_global_options(self):
|
12
|
+
path = "prompt-path"
|
13
|
+
messages = self.create_conversation(4)
|
14
|
+
options = CreateLogOptions(response="response")
|
15
|
+
endpoint = f"/projects/{self.project_id}/versions/{self.version_uuid}/documents/logs"
|
16
|
+
endpoint_mock = self.gateway_mock.post(endpoint).mock(
|
17
|
+
return_value=httpx.Response(200, json=fixtures.LOG_RESPONSE)
|
18
|
+
)
|
19
|
+
|
20
|
+
result = await self.sdk.logs.create(path, messages, options)
|
21
|
+
request, _ = endpoint_mock.calls.last
|
22
|
+
|
23
|
+
self.assert_requested(
|
24
|
+
request,
|
25
|
+
method="POST",
|
26
|
+
endpoint=endpoint,
|
27
|
+
body={
|
28
|
+
"path": path,
|
29
|
+
"messages": [json.loads(message.model_dump_json()) for message in messages],
|
30
|
+
"response": options.response,
|
31
|
+
"__internal": {"source": "api"},
|
32
|
+
},
|
33
|
+
)
|
34
|
+
self.assertEqual(endpoint_mock.call_count, 1)
|
35
|
+
self.assertEqual(result, CreateLogResult(**dict(fixtures.LOG)))
|
36
|
+
|
37
|
+
async def test_success_overrides_options(self):
|
38
|
+
path = "prompt-path"
|
39
|
+
messages = self.create_conversation(4)
|
40
|
+
options = CreateLogOptions(project_id=21, version_uuid="version-uuid", response="response")
|
41
|
+
endpoint = f"/projects/{options.project_id}/versions/{options.version_uuid}/documents/logs"
|
42
|
+
endpoint_mock = self.gateway_mock.post(endpoint).mock(
|
43
|
+
return_value=httpx.Response(200, json=fixtures.LOG_RESPONSE)
|
44
|
+
)
|
45
|
+
|
46
|
+
result = await self.sdk.logs.create(path, messages, options)
|
47
|
+
request, _ = endpoint_mock.calls.last
|
48
|
+
|
49
|
+
self.assert_requested(
|
50
|
+
request,
|
51
|
+
method="POST",
|
52
|
+
endpoint=endpoint,
|
53
|
+
body={
|
54
|
+
"path": path,
|
55
|
+
"messages": [json.loads(message.model_dump_json()) for message in messages],
|
56
|
+
"response": options.response,
|
57
|
+
"__internal": {"source": "api"},
|
58
|
+
},
|
59
|
+
)
|
60
|
+
self.assertEqual(endpoint_mock.call_count, 1)
|
61
|
+
self.assertEqual(result, CreateLogResult(**dict(fixtures.LOG)))
|
62
|
+
|
63
|
+
async def test_success_default_version_uuid(self):
|
64
|
+
self.sdk._options.version_uuid = None # pyright: ignore [reportPrivateUsage]
|
65
|
+
path = "prompt-path"
|
66
|
+
messages = self.create_conversation(4)
|
67
|
+
options = CreateLogOptions(response="response")
|
68
|
+
endpoint = f"/projects/{self.project_id}/versions/live/documents/logs"
|
69
|
+
endpoint_mock = self.gateway_mock.post(endpoint).mock(
|
70
|
+
return_value=httpx.Response(200, json=fixtures.LOG_RESPONSE)
|
71
|
+
)
|
72
|
+
|
73
|
+
result = await self.sdk.logs.create(path, messages, options)
|
74
|
+
request, _ = endpoint_mock.calls.last
|
75
|
+
|
76
|
+
self.assert_requested(
|
77
|
+
request,
|
78
|
+
method="POST",
|
79
|
+
endpoint=endpoint,
|
80
|
+
body={
|
81
|
+
"path": path,
|
82
|
+
"messages": [json.loads(message.model_dump_json()) for message in messages],
|
83
|
+
"response": options.response,
|
84
|
+
"__internal": {"source": "api"},
|
85
|
+
},
|
86
|
+
)
|
87
|
+
self.assertEqual(endpoint_mock.call_count, 1)
|
88
|
+
self.assertEqual(result, CreateLogResult(**dict(fixtures.LOG)))
|
89
|
+
|
90
|
+
async def test_fails(self):
|
91
|
+
path = "prompt-path"
|
92
|
+
messages = self.create_conversation(4)
|
93
|
+
options = CreateLogOptions(response="response")
|
94
|
+
endpoint = f"/projects/{self.project_id}/versions/{self.version_uuid}/documents/logs"
|
95
|
+
endpoint_mock = self.gateway_mock.post(endpoint).mock(
|
96
|
+
return_value=httpx.Response(500, json=fixtures.ERROR_RESPONSE)
|
97
|
+
)
|
98
|
+
|
99
|
+
with self.assertRaisesRegex(type(fixtures.ERROR), fixtures.ERROR.message):
|
100
|
+
await self.sdk.logs.create(path, messages, options)
|
101
|
+
requests = cast(List[httpx.Request], [request for request, _ in endpoint_mock.calls]) # type: ignore
|
102
|
+
|
103
|
+
[
|
104
|
+
self.assert_requested(
|
105
|
+
request,
|
106
|
+
method="POST",
|
107
|
+
endpoint=endpoint,
|
108
|
+
body={
|
109
|
+
"path": path,
|
110
|
+
"messages": [json.loads(message.model_dump_json()) for message in messages],
|
111
|
+
"response": options.response,
|
112
|
+
"__internal": {"source": "api"},
|
113
|
+
},
|
114
|
+
)
|
115
|
+
for request in requests
|
116
|
+
]
|
117
|
+
self.assertEqual(endpoint_mock.call_count, self.internal_options.retries)
|
File without changes
|