tracia 0.0.1__py3-none-any.whl → 0.1.0__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.
@@ -0,0 +1,273 @@
1
+ """Prompts API resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from urllib.parse import quote
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from .._types import (
9
+ CreatePromptOptions,
10
+ Prompt,
11
+ PromptListItem,
12
+ RunOptions,
13
+ RunResult,
14
+ UpdatePromptOptions,
15
+ )
16
+
17
+ if TYPE_CHECKING:
18
+ from .._http import AsyncHttpClient, HttpClient
19
+
20
+
21
+ class Prompts:
22
+ """Prompts API resource for managing prompt templates."""
23
+
24
+ def __init__(
25
+ self,
26
+ client: "HttpClient",
27
+ async_client: "AsyncHttpClient | None" = None,
28
+ ) -> None:
29
+ """Initialize the Prompts resource.
30
+
31
+ Args:
32
+ client: The synchronous HTTP client.
33
+ async_client: Optional async HTTP client.
34
+ """
35
+ self._client = client
36
+ self._async_client = async_client
37
+
38
+ def list(self) -> list[PromptListItem]:
39
+ """List all prompts.
40
+
41
+ Returns:
42
+ The list of prompts.
43
+
44
+ Raises:
45
+ TraciaError: If the request fails.
46
+ """
47
+ data = self._client.get("/api/v1/prompts")
48
+ return [PromptListItem.model_validate(item) for item in data]
49
+
50
+ async def alist(self) -> list[PromptListItem]:
51
+ """List all prompts asynchronously.
52
+
53
+ Returns:
54
+ The list of prompts.
55
+
56
+ Raises:
57
+ TraciaError: If the request fails.
58
+ """
59
+ if self._async_client is None:
60
+ raise RuntimeError("Async client not initialized")
61
+
62
+ data = await self._async_client.get("/api/v1/prompts")
63
+ return [PromptListItem.model_validate(item) for item in data]
64
+
65
+ def get(self, slug: str) -> Prompt:
66
+ """Get a prompt by slug.
67
+
68
+ Args:
69
+ slug: The prompt slug.
70
+
71
+ Returns:
72
+ The prompt.
73
+
74
+ Raises:
75
+ TraciaError: If the request fails.
76
+ """
77
+ data = self._client.get(f"/api/v1/prompts/{quote(slug, safe='')}")
78
+ return Prompt.model_validate(data)
79
+
80
+ async def aget(self, slug: str) -> Prompt:
81
+ """Get a prompt by slug asynchronously.
82
+
83
+ Args:
84
+ slug: The prompt slug.
85
+
86
+ Returns:
87
+ The prompt.
88
+
89
+ Raises:
90
+ TraciaError: If the request fails.
91
+ """
92
+ if self._async_client is None:
93
+ raise RuntimeError("Async client not initialized")
94
+
95
+ data = await self._async_client.get(f"/api/v1/prompts/{quote(slug, safe='')}")
96
+ return Prompt.model_validate(data)
97
+
98
+ def create(self, options: CreatePromptOptions) -> Prompt:
99
+ """Create a new prompt.
100
+
101
+ Args:
102
+ options: The prompt creation options.
103
+
104
+ Returns:
105
+ The created prompt.
106
+
107
+ Raises:
108
+ TraciaError: If the request fails.
109
+ """
110
+ data = self._client.post(
111
+ "/api/v1/prompts",
112
+ options.model_dump(by_alias=True, exclude_none=True),
113
+ )
114
+ return Prompt.model_validate(data)
115
+
116
+ async def acreate(self, options: CreatePromptOptions) -> Prompt:
117
+ """Create a new prompt asynchronously.
118
+
119
+ Args:
120
+ options: The prompt creation options.
121
+
122
+ Returns:
123
+ The created prompt.
124
+
125
+ Raises:
126
+ TraciaError: If the request fails.
127
+ """
128
+ if self._async_client is None:
129
+ raise RuntimeError("Async client not initialized")
130
+
131
+ data = await self._async_client.post(
132
+ "/api/v1/prompts",
133
+ options.model_dump(by_alias=True, exclude_none=True),
134
+ )
135
+ return Prompt.model_validate(data)
136
+
137
+ def update(self, slug: str, options: UpdatePromptOptions) -> Prompt:
138
+ """Update an existing prompt.
139
+
140
+ Args:
141
+ slug: The prompt slug.
142
+ options: The update options.
143
+
144
+ Returns:
145
+ The updated prompt.
146
+
147
+ Raises:
148
+ TraciaError: If the request fails.
149
+ """
150
+ data = self._client.put(
151
+ f"/api/v1/prompts/{quote(slug, safe='')}",
152
+ options.model_dump(by_alias=True, exclude_none=True),
153
+ )
154
+ return Prompt.model_validate(data)
155
+
156
+ async def aupdate(self, slug: str, options: UpdatePromptOptions) -> Prompt:
157
+ """Update an existing prompt asynchronously.
158
+
159
+ Args:
160
+ slug: The prompt slug.
161
+ options: The update options.
162
+
163
+ Returns:
164
+ The updated prompt.
165
+
166
+ Raises:
167
+ TraciaError: If the request fails.
168
+ """
169
+ if self._async_client is None:
170
+ raise RuntimeError("Async client not initialized")
171
+
172
+ data = await self._async_client.put(
173
+ f"/api/v1/prompts/{quote(slug, safe='')}",
174
+ options.model_dump(by_alias=True, exclude_none=True),
175
+ )
176
+ return Prompt.model_validate(data)
177
+
178
+ def delete(self, slug: str) -> None:
179
+ """Delete a prompt.
180
+
181
+ Args:
182
+ slug: The prompt slug.
183
+
184
+ Raises:
185
+ TraciaError: If the request fails.
186
+ """
187
+ self._client.delete(f"/api/v1/prompts/{quote(slug, safe='')}")
188
+
189
+ async def adelete(self, slug: str) -> None:
190
+ """Delete a prompt asynchronously.
191
+
192
+ Args:
193
+ slug: The prompt slug.
194
+
195
+ Raises:
196
+ TraciaError: If the request fails.
197
+ """
198
+ if self._async_client is None:
199
+ raise RuntimeError("Async client not initialized")
200
+
201
+ await self._async_client.delete(f"/api/v1/prompts/{quote(slug, safe='')}")
202
+
203
+ def run(
204
+ self,
205
+ slug: str,
206
+ variables: dict[str, str] | None = None,
207
+ options: RunOptions | None = None,
208
+ ) -> RunResult:
209
+ """Run a prompt template.
210
+
211
+ Args:
212
+ slug: The prompt slug.
213
+ variables: Optional variables to interpolate.
214
+ options: Optional run options.
215
+
216
+ Returns:
217
+ The run result.
218
+
219
+ Raises:
220
+ TraciaError: If the request fails.
221
+ """
222
+ body = self._build_run_body(variables, options)
223
+ data = self._client.post(f"/api/v1/prompts/{quote(slug, safe='')}/run", body)
224
+ return RunResult.model_validate(data)
225
+
226
+ async def arun(
227
+ self,
228
+ slug: str,
229
+ variables: dict[str, str] | None = None,
230
+ options: RunOptions | None = None,
231
+ ) -> RunResult:
232
+ """Run a prompt template asynchronously.
233
+
234
+ Args:
235
+ slug: The prompt slug.
236
+ variables: Optional variables to interpolate.
237
+ options: Optional run options.
238
+
239
+ Returns:
240
+ The run result.
241
+
242
+ Raises:
243
+ TraciaError: If the request fails.
244
+ """
245
+ if self._async_client is None:
246
+ raise RuntimeError("Async client not initialized")
247
+
248
+ body = self._build_run_body(variables, options)
249
+ data = await self._async_client.post(f"/api/v1/prompts/{quote(slug, safe='')}/run", body)
250
+ return RunResult.model_validate(data)
251
+
252
+ def _build_run_body(
253
+ self,
254
+ variables: dict[str, str] | None,
255
+ options: RunOptions | None,
256
+ ) -> dict[str, Any]:
257
+ """Build the request body for the run endpoint."""
258
+ body: dict[str, Any] = {}
259
+
260
+ if variables:
261
+ body["variables"] = variables
262
+
263
+ if options:
264
+ if options.model is not None:
265
+ body["model"] = options.model
266
+ if options.tags is not None:
267
+ body["tags"] = options.tags
268
+ if options.user_id is not None:
269
+ body["userId"] = options.user_id
270
+ if options.session_id is not None:
271
+ body["sessionId"] = options.session_id
272
+
273
+ return body
@@ -0,0 +1,227 @@
1
+ """Spans API resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from urllib.parse import quote
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from .._types import (
9
+ CreateSpanPayload,
10
+ CreateSpanResult,
11
+ EvaluateOptions,
12
+ EvaluateResult,
13
+ ListSpansOptions,
14
+ ListSpansResult,
15
+ Span,
16
+ )
17
+
18
+ if TYPE_CHECKING:
19
+ from .._http import AsyncHttpClient, HttpClient
20
+
21
+
22
+ class Spans:
23
+ """Spans API resource for managing trace spans."""
24
+
25
+ def __init__(
26
+ self,
27
+ client: "HttpClient",
28
+ async_client: "AsyncHttpClient | None" = None,
29
+ ) -> None:
30
+ """Initialize the Spans resource.
31
+
32
+ Args:
33
+ client: The synchronous HTTP client.
34
+ async_client: Optional async HTTP client.
35
+ """
36
+ self._client = client
37
+ self._async_client = async_client
38
+
39
+ def create(self, payload: CreateSpanPayload) -> CreateSpanResult:
40
+ """Create a new span.
41
+
42
+ Args:
43
+ payload: The span creation payload.
44
+
45
+ Returns:
46
+ The created span result.
47
+
48
+ Raises:
49
+ TraciaError: If the request fails.
50
+ """
51
+ data = self._client.post(
52
+ "/api/v1/spans",
53
+ payload.model_dump(by_alias=True, exclude_none=True),
54
+ )
55
+ return CreateSpanResult.model_validate(data)
56
+
57
+ async def acreate(self, payload: CreateSpanPayload) -> CreateSpanResult:
58
+ """Create a new span asynchronously.
59
+
60
+ Args:
61
+ payload: The span creation payload.
62
+
63
+ Returns:
64
+ The created span result.
65
+
66
+ Raises:
67
+ TraciaError: If the request fails.
68
+ """
69
+ if self._async_client is None:
70
+ raise RuntimeError("Async client not initialized")
71
+
72
+ data = await self._async_client.post(
73
+ "/api/v1/spans",
74
+ payload.model_dump(by_alias=True, exclude_none=True),
75
+ )
76
+ return CreateSpanResult.model_validate(data)
77
+
78
+ def get(self, span_id: str) -> Span:
79
+ """Get a span by ID.
80
+
81
+ Args:
82
+ span_id: The span ID.
83
+
84
+ Returns:
85
+ The span.
86
+
87
+ Raises:
88
+ TraciaError: If the request fails.
89
+ """
90
+ data = self._client.get(f"/api/v1/spans/{quote(span_id, safe='')}")
91
+ return Span.model_validate(data)
92
+
93
+ async def aget(self, span_id: str) -> Span:
94
+ """Get a span by ID asynchronously.
95
+
96
+ Args:
97
+ span_id: The span ID.
98
+
99
+ Returns:
100
+ The span.
101
+
102
+ Raises:
103
+ TraciaError: If the request fails.
104
+ """
105
+ if self._async_client is None:
106
+ raise RuntimeError("Async client not initialized")
107
+
108
+ data = await self._async_client.get(f"/api/v1/spans/{quote(span_id, safe='')}")
109
+ return Span.model_validate(data)
110
+
111
+ def list(self, options: ListSpansOptions | None = None) -> ListSpansResult:
112
+ """List spans with optional filtering.
113
+
114
+ Args:
115
+ options: Optional filtering and pagination options.
116
+
117
+ Returns:
118
+ The list of spans.
119
+
120
+ Raises:
121
+ TraciaError: If the request fails.
122
+ """
123
+ params = self._build_list_params(options)
124
+ data = self._client.get("/api/v1/spans", params=params)
125
+ return ListSpansResult.model_validate(data)
126
+
127
+ async def alist(self, options: ListSpansOptions | None = None) -> ListSpansResult:
128
+ """List spans asynchronously with optional filtering.
129
+
130
+ Args:
131
+ options: Optional filtering and pagination options.
132
+
133
+ Returns:
134
+ The list of spans.
135
+
136
+ Raises:
137
+ TraciaError: If the request fails.
138
+ """
139
+ if self._async_client is None:
140
+ raise RuntimeError("Async client not initialized")
141
+
142
+ params = self._build_list_params(options)
143
+ data = await self._async_client.get("/api/v1/spans", params=params)
144
+ return ListSpansResult.model_validate(data)
145
+
146
+ def _build_list_params(
147
+ self, options: ListSpansOptions | None
148
+ ) -> dict[str, Any] | None:
149
+ """Build query parameters for list endpoint."""
150
+ if options is None:
151
+ return None
152
+
153
+ params: dict[str, Any] = {}
154
+
155
+ if options.prompt_slug is not None:
156
+ params["promptSlug"] = options.prompt_slug
157
+ if options.status is not None:
158
+ params["status"] = options.status
159
+ if options.start_date is not None:
160
+ params["startDate"] = options.start_date.isoformat()
161
+ if options.end_date is not None:
162
+ params["endDate"] = options.end_date.isoformat()
163
+ if options.user_id is not None:
164
+ params["userId"] = options.user_id
165
+ if options.session_id is not None:
166
+ params["sessionId"] = options.session_id
167
+ if options.tags is not None and len(options.tags) > 0:
168
+ params["tags"] = ",".join(options.tags)
169
+ if options.limit is not None:
170
+ params["limit"] = options.limit
171
+ if options.cursor is not None:
172
+ params["cursor"] = options.cursor
173
+
174
+ return params if params else None
175
+
176
+ def evaluate(self, span_id: str, options: EvaluateOptions) -> EvaluateResult:
177
+ """Evaluate a span.
178
+
179
+ Args:
180
+ span_id: The span ID to evaluate.
181
+ options: The evaluation options.
182
+
183
+ Returns:
184
+ The evaluation result.
185
+
186
+ Raises:
187
+ TraciaError: If the request fails.
188
+ """
189
+ body = {
190
+ "evaluatorKey": options.evaluator,
191
+ "value": options.value,
192
+ }
193
+ if options.note is not None:
194
+ body["note"] = options.note
195
+
196
+ data = self._client.post(f"/api/v1/spans/{quote(span_id, safe='')}/evaluations", body)
197
+ return EvaluateResult.model_validate(data)
198
+
199
+ async def aevaluate(
200
+ self, span_id: str, options: EvaluateOptions
201
+ ) -> EvaluateResult:
202
+ """Evaluate a span asynchronously.
203
+
204
+ Args:
205
+ span_id: The span ID to evaluate.
206
+ options: The evaluation options.
207
+
208
+ Returns:
209
+ The evaluation result.
210
+
211
+ Raises:
212
+ TraciaError: If the request fails.
213
+ """
214
+ if self._async_client is None:
215
+ raise RuntimeError("Async client not initialized")
216
+
217
+ body = {
218
+ "evaluatorKey": options.evaluator,
219
+ "value": options.value,
220
+ }
221
+ if options.note is not None:
222
+ body["note"] = options.note
223
+
224
+ data = await self._async_client.post(
225
+ f"/api/v1/spans/{quote(span_id, safe='')}/evaluations", body
226
+ )
227
+ return EvaluateResult.model_validate(data)