docent-python 0.1.59a0__tar.gz → 0.1.61a0__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.
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/PKG-INFO +1 -1
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/reading.py +178 -4
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/mcp/server.py +13 -3
- docent_python-0.1.61a0/docent/sdk/_agent_runs.py +217 -0
- docent_python-0.1.61a0/docent/sdk/_base.py +299 -0
- docent_python-0.1.61a0/docent/sdk/_client_util.py +141 -0
- docent_python-0.1.61a0/docent/sdk/_collections.py +421 -0
- docent_python-0.1.61a0/docent/sdk/_dql.py +304 -0
- docent_python-0.1.61a0/docent/sdk/_feedback.py +157 -0
- docent_python-0.1.61a0/docent/sdk/_labels.py +225 -0
- docent_python-0.1.61a0/docent/sdk/_readings.py +1178 -0
- docent_python-0.1.61a0/docent/sdk/_results.py +311 -0
- docent_python-0.1.61a0/docent/sdk/_rubrics.py +320 -0
- docent_python-0.1.61a0/docent/sdk/_sharing.py +229 -0
- docent_python-0.1.61a0/docent/sdk/client.py +45 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/reading.py +20 -5
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/pyproject.toml +1 -1
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/uv.lock +1 -1
- docent_python-0.1.59a0/docent/sdk/client.py +0 -3281
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/.gitignore +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/LICENSE.md +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/README.md +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/data_models/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/data_models/exceptions.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/data_models/llm_output.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/llm_cache.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/llm_svc.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/model_registry.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/providers/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/providers/anthropic.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/providers/common.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/providers/google.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/providers/openai.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/providers/openrouter.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/providers/preference_types.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_llm_util/providers/provider_registry.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_log_util/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/_log_util/logger.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/_tiktoken_util.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/agent_run.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/chat/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/chat/content.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/chat/message.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/chat/response_format.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/chat/tool.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/citation.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/feedback.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/formatted_objects.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/judge.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/metadata_util.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/regex.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/transcript.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/data_models/util.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/analysis.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/impl.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/runner.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/stats.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/types.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/util/forgiving_json.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/util/meta_schema.json +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/util/meta_schema.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/util/parse_output.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/util/template_formatter.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/judges/util/voting.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/loaders/load_inspect.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/mcp/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/mcp/__main__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/py.typed +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/samples/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/samples/load.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/samples/log.eval +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/samples/tb_airline.json +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/agent_run_writer.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/integrations/__init__.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/integrations/harbor.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/integrations/inspect.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/integrations/nemogym.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/integrations/util.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/llm_context.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/llm_request.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/sdk/util.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/trace.py +0 -0
- {docent_python-0.1.59a0 → docent_python-0.1.61a0}/docent/trace_temp.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from typing import Any, Literal, TypeAlias
|
|
2
|
+
from typing import Annotated, Any, Literal, TypeAlias
|
|
3
3
|
from uuid import uuid4
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
5
|
+
from pydantic import BaseModel, Field, model_validator
|
|
6
6
|
|
|
7
7
|
from docent._llm_util.providers.preference_types import ModelOption
|
|
8
8
|
|
|
@@ -41,6 +41,7 @@ prompt segments, model config, output schema, and user-supplied arguments.
|
|
|
41
41
|
re-evaluation.
|
|
42
42
|
"""
|
|
43
43
|
ReadingCacheMode = Literal["reading", "results", "none"]
|
|
44
|
+
ReadingStatus = Literal["completed", "failed", "pending", "cached", "needs_approval", "unresolved"]
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
class ContextFilterSection(BaseModel):
|
|
@@ -73,6 +74,7 @@ class ReadingPreset(BaseModel):
|
|
|
73
74
|
collection_id: str
|
|
74
75
|
name: str
|
|
75
76
|
created_at: datetime | None = None
|
|
77
|
+
created_by: str | None = None
|
|
76
78
|
updated_at: datetime | None = None
|
|
77
79
|
|
|
78
80
|
|
|
@@ -117,6 +119,7 @@ class Reading(BaseModel):
|
|
|
117
119
|
user_metadata: dict[str, Any] | None = None
|
|
118
120
|
source_reading_preset_id: str | None = None
|
|
119
121
|
created_at: datetime | None = None
|
|
122
|
+
created_by: str | None = None
|
|
120
123
|
|
|
121
124
|
|
|
122
125
|
class ReadingResult(BaseModel):
|
|
@@ -174,6 +177,7 @@ class ReadingStep(BaseModel):
|
|
|
174
177
|
name: str | None = None
|
|
175
178
|
reading_id: str | None = None
|
|
176
179
|
dql_query: str | None = None
|
|
180
|
+
dql_step_alias: str | None = None
|
|
177
181
|
prompt_template_segments: list[Any] | None = None
|
|
178
182
|
context_config: dict[str, Any] | None = None
|
|
179
183
|
model: ModelOption
|
|
@@ -188,7 +192,11 @@ class ReadingStep(BaseModel):
|
|
|
188
192
|
def to_submission(self, *, dql_query: str | None = None) -> "ReadingStepSubmission":
|
|
189
193
|
"""Convert to a ReadingStepSubmission for resolve_reading_entry.
|
|
190
194
|
|
|
191
|
-
Optionally overrides dql_query (e.g. after alias substitution).
|
|
195
|
+
Optionally overrides dql_query (e.g. after alias substitution). The
|
|
196
|
+
stored step's own dql_query may be None when dql_step_alias is set;
|
|
197
|
+
callers are expected to pass the resolved DQL explicitly in that case.
|
|
198
|
+
When a concrete DQL is supplied, clear dql_step_alias so the
|
|
199
|
+
submission continues to satisfy the "exactly one DQL source" contract.
|
|
192
200
|
"""
|
|
193
201
|
return ReadingStepSubmission(
|
|
194
202
|
alias=self.alias,
|
|
@@ -200,6 +208,7 @@ class ReadingStep(BaseModel):
|
|
|
200
208
|
prompt_template_segments=self.prompt_template_segments,
|
|
201
209
|
context_config=self.context_config,
|
|
202
210
|
dql_query=dql_query if dql_query is not None else self.dql_query,
|
|
211
|
+
dql_step_alias=None if dql_query is not None else self.dql_step_alias,
|
|
203
212
|
source_reading_preset_id=self.source_reading_preset_id,
|
|
204
213
|
cache_mode=self.cache_mode,
|
|
205
214
|
)
|
|
@@ -226,6 +235,7 @@ class ReadingPlan(BaseModel):
|
|
|
226
235
|
name: str | None = None
|
|
227
236
|
steps: list[PlanStep] = Field(default_factory=list) # type: ignore[reportUnknownVariableType]
|
|
228
237
|
created_at: datetime | None = None
|
|
238
|
+
created_by: str | None = None
|
|
229
239
|
updated_at: datetime | None = None
|
|
230
240
|
|
|
231
241
|
|
|
@@ -268,10 +278,29 @@ class ReadingStepSubmission(BaseModel):
|
|
|
268
278
|
prompt_template_segments: list[Any] | None = None
|
|
269
279
|
context_config: dict[str, ParameterContextConfig] | None = None
|
|
270
280
|
dql_query: str | None = None
|
|
281
|
+
# References a DqlOnlyStep in the same plan whose rows feed this reading.
|
|
282
|
+
# Mutually exclusive with dql_query for template entries.
|
|
283
|
+
dql_step_alias: str | None = None
|
|
271
284
|
|
|
272
285
|
# Scripted reading fields (mutually exclusive with template fields)
|
|
273
286
|
requests: list[ScriptedRequest] | None = None
|
|
274
287
|
|
|
288
|
+
@model_validator(mode="after")
|
|
289
|
+
def _validate_dql_source(self) -> "ReadingStepSubmission":
|
|
290
|
+
if self.requests is not None:
|
|
291
|
+
if self.dql_query is not None or self.dql_step_alias is not None:
|
|
292
|
+
raise ValueError(
|
|
293
|
+
"Scripted reading submissions must not set dql_query or dql_step_alias"
|
|
294
|
+
)
|
|
295
|
+
return self
|
|
296
|
+
if self.dql_query is not None and self.dql_step_alias is not None:
|
|
297
|
+
raise ValueError("ReadingStepSubmission: set exactly one of dql_query / dql_step_alias")
|
|
298
|
+
if self.dql_query is None and self.dql_step_alias is None:
|
|
299
|
+
raise ValueError(
|
|
300
|
+
"ReadingStepSubmission: template entries must set one of dql_query / dql_step_alias"
|
|
301
|
+
)
|
|
302
|
+
return self
|
|
303
|
+
|
|
275
304
|
|
|
276
305
|
class PresetReadingStepSubmission(BaseModel):
|
|
277
306
|
entry_type: Literal["preset_reading"] = "preset_reading"
|
|
@@ -280,8 +309,21 @@ class PresetReadingStepSubmission(BaseModel):
|
|
|
280
309
|
source_reading_preset_id: str
|
|
281
310
|
user_metadata: dict[str, Any] | None = None
|
|
282
311
|
dql_query: str | None = None
|
|
312
|
+
dql_step_alias: str | None = None
|
|
283
313
|
cache_mode: ReadingCacheMode = "reading"
|
|
284
314
|
|
|
315
|
+
@model_validator(mode="after")
|
|
316
|
+
def _validate_dql_source(self) -> "PresetReadingStepSubmission":
|
|
317
|
+
if self.dql_query is not None and self.dql_step_alias is not None:
|
|
318
|
+
raise ValueError(
|
|
319
|
+
"PresetReadingStepSubmission: set exactly one of dql_query / dql_step_alias"
|
|
320
|
+
)
|
|
321
|
+
if self.dql_query is None and self.dql_step_alias is None:
|
|
322
|
+
raise ValueError(
|
|
323
|
+
"PresetReadingStepSubmission: must set one of dql_query / dql_step_alias"
|
|
324
|
+
)
|
|
325
|
+
return self
|
|
326
|
+
|
|
285
327
|
|
|
286
328
|
class DqlOnlyStepSubmission(BaseModel):
|
|
287
329
|
entry_type: Literal["dql_only"] = "dql_only"
|
|
@@ -306,23 +348,140 @@ class PlanSubmissionRequest(BaseModel):
|
|
|
306
348
|
entries: list[PlanStepSubmission]
|
|
307
349
|
|
|
308
350
|
|
|
351
|
+
class DqlPreview(BaseModel):
|
|
352
|
+
columns: list[str]
|
|
353
|
+
rows: list[list[Any]]
|
|
354
|
+
truncated: bool
|
|
355
|
+
row_count: int
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class ReadingResultPreview(BaseModel):
|
|
359
|
+
id: str
|
|
360
|
+
output: dict[str, Any] | None = None
|
|
361
|
+
error: dict[str, Any] | None = None
|
|
362
|
+
|
|
363
|
+
|
|
309
364
|
class PlanStepSubmissionStatus(BaseModel):
|
|
310
365
|
alias: str
|
|
311
|
-
|
|
366
|
+
entry_type: str
|
|
367
|
+
status: ReadingStatus
|
|
312
368
|
reading_id: str | None = None
|
|
369
|
+
result_count: int | None = None
|
|
370
|
+
dql_preview: DqlPreview | None = None
|
|
371
|
+
result_preview: list[ReadingResultPreview] | None = None
|
|
313
372
|
|
|
314
373
|
|
|
315
374
|
class PlanSubmissionResponse(BaseModel):
|
|
316
375
|
plan_id: str
|
|
376
|
+
plan_name: str | None = None
|
|
377
|
+
previous_latest_plan_id: str | None = None
|
|
378
|
+
has_active_listeners: bool = False
|
|
317
379
|
entry_statuses: list[PlanStepSubmissionStatus]
|
|
318
380
|
|
|
319
381
|
|
|
382
|
+
# ── Plan SSE stream events (server → SDK) ────────────────────────────
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
class PlanStreamStepStatus(BaseModel):
|
|
386
|
+
"""Minimal step shape carried inside a snapshot event."""
|
|
387
|
+
|
|
388
|
+
alias: str
|
|
389
|
+
reading_id: str | None = None
|
|
390
|
+
derived_status: str
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
class PlanSnapshotEvent(BaseModel):
|
|
394
|
+
type: Literal["snapshot"] = "snapshot"
|
|
395
|
+
steps: list[PlanStreamStepStatus]
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
class PlanStepError(BaseModel):
|
|
399
|
+
message: str
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class PlanStepStartedEvent(BaseModel):
|
|
403
|
+
type: Literal["step_started"] = "step_started"
|
|
404
|
+
plan_id: str
|
|
405
|
+
step_alias: str
|
|
406
|
+
job_id: str
|
|
407
|
+
reading_id: str
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class PlanStepCompletedEvent(BaseModel):
|
|
411
|
+
type: Literal["step_completed"] = "step_completed"
|
|
412
|
+
plan_id: str
|
|
413
|
+
step_alias: str
|
|
414
|
+
job_id: str
|
|
415
|
+
reading_id: str
|
|
416
|
+
result_count: int | None = None
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class PlanStepFailedEvent(BaseModel):
|
|
420
|
+
type: Literal["step_failed"] = "step_failed"
|
|
421
|
+
plan_id: str
|
|
422
|
+
step_alias: str
|
|
423
|
+
job_id: str
|
|
424
|
+
error: PlanStepError | None = None
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
class PlanStepsUpdatedEvent(BaseModel):
|
|
428
|
+
type: Literal["steps_updated"] = "steps_updated"
|
|
429
|
+
plan_id: str
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
class PlanJobStartedEvent(BaseModel):
|
|
433
|
+
type: Literal["job_started"] = "job_started"
|
|
434
|
+
plan_id: str
|
|
435
|
+
job_id: str
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
class PlanJobCompletedEvent(BaseModel):
|
|
439
|
+
type: Literal["job_completed"] = "job_completed"
|
|
440
|
+
plan_id: str
|
|
441
|
+
job_id: str
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class PlanJobFailedEvent(BaseModel):
|
|
445
|
+
type: Literal["job_failed"] = "job_failed"
|
|
446
|
+
plan_id: str
|
|
447
|
+
job_id: str
|
|
448
|
+
error: PlanStepError | None = None
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class PlanSupersededEvent(BaseModel):
|
|
452
|
+
type: Literal["plan_superseded"] = "plan_superseded"
|
|
453
|
+
plan_id: str
|
|
454
|
+
superseded_by_plan_id: str
|
|
455
|
+
name: str | None = None
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
class PlanJobCancelledEvent(BaseModel):
|
|
459
|
+
type: Literal["job_cancelled"] = "job_cancelled"
|
|
460
|
+
plan_id: str
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
PlanStreamEvent: TypeAlias = Annotated[
|
|
464
|
+
PlanSnapshotEvent
|
|
465
|
+
| PlanStepStartedEvent
|
|
466
|
+
| PlanStepCompletedEvent
|
|
467
|
+
| PlanStepFailedEvent
|
|
468
|
+
| PlanStepsUpdatedEvent
|
|
469
|
+
| PlanJobStartedEvent
|
|
470
|
+
| PlanJobCompletedEvent
|
|
471
|
+
| PlanJobFailedEvent
|
|
472
|
+
| PlanJobCancelledEvent
|
|
473
|
+
| PlanSupersededEvent,
|
|
474
|
+
Field(discriminator="type"),
|
|
475
|
+
]
|
|
476
|
+
|
|
477
|
+
|
|
320
478
|
__all__ = [
|
|
321
479
|
"AnnotatableReadingParamType",
|
|
322
480
|
"BeginGroupStep",
|
|
323
481
|
"ContextFilterSection",
|
|
324
482
|
"DqlOnlyStep",
|
|
325
483
|
"DqlOnlyStepSubmission",
|
|
484
|
+
"DqlPreview",
|
|
326
485
|
"EndGroupStep",
|
|
327
486
|
"EndStepGroupSubmission",
|
|
328
487
|
"ScriptedRequest",
|
|
@@ -335,6 +494,8 @@ __all__ = [
|
|
|
335
494
|
"ReadingCacheMode",
|
|
336
495
|
"ReadingParamPlaceholder",
|
|
337
496
|
"ReadingParamType",
|
|
497
|
+
"ReadingResultPreview",
|
|
498
|
+
"ReadingStatus",
|
|
338
499
|
"ReadingStep",
|
|
339
500
|
"ReadingStepSubmission",
|
|
340
501
|
"ReadingTemplateSegment",
|
|
@@ -346,4 +507,17 @@ __all__ = [
|
|
|
346
507
|
"ReadingResult",
|
|
347
508
|
"StepGroupSubmission",
|
|
348
509
|
"PresetReadingStepSubmission",
|
|
510
|
+
"PlanStreamEvent",
|
|
511
|
+
"PlanStreamStepStatus",
|
|
512
|
+
"PlanSnapshotEvent",
|
|
513
|
+
"PlanStepStartedEvent",
|
|
514
|
+
"PlanStepCompletedEvent",
|
|
515
|
+
"PlanStepError",
|
|
516
|
+
"PlanStepFailedEvent",
|
|
517
|
+
"PlanStepsUpdatedEvent",
|
|
518
|
+
"PlanJobStartedEvent",
|
|
519
|
+
"PlanJobCompletedEvent",
|
|
520
|
+
"PlanJobFailedEvent",
|
|
521
|
+
"PlanJobCancelledEvent",
|
|
522
|
+
"PlanSupersededEvent",
|
|
349
523
|
]
|
|
@@ -33,10 +33,13 @@ def get_metadata_fields(collection_id: str) -> str:
|
|
|
33
33
|
"""
|
|
34
34
|
client = get_client()
|
|
35
35
|
try:
|
|
36
|
-
|
|
36
|
+
response = client.get_metadata_fields(
|
|
37
37
|
collection_id, include_sample_values=True, sample_limit=10
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
+
fields = response.get("fields", [])
|
|
41
|
+
total_runs = response.get("total_runs")
|
|
42
|
+
|
|
40
43
|
if not fields:
|
|
41
44
|
return f"No metadata fields found for collection {collection_id}"
|
|
42
45
|
|
|
@@ -71,7 +74,10 @@ def get_metadata_fields(collection_id: str) -> str:
|
|
|
71
74
|
lines.append(line)
|
|
72
75
|
|
|
73
76
|
field_list = "\n".join(lines)
|
|
74
|
-
|
|
77
|
+
tool_output = f"Metadata fields for collection {collection_id}:\n{field_list}"
|
|
78
|
+
if total_runs is not None:
|
|
79
|
+
tool_output += f"\n\nTotal runs: {total_runs}"
|
|
80
|
+
return tool_output
|
|
75
81
|
except Exception as e:
|
|
76
82
|
error_msg = str(e)
|
|
77
83
|
if "404" in error_msg:
|
|
@@ -321,7 +327,11 @@ def get_reading_plan_results(
|
|
|
321
327
|
for step in steps:
|
|
322
328
|
if step.get("type") == "reading" and step.get("reading_id"):
|
|
323
329
|
try:
|
|
324
|
-
results = client.get_reading_results(
|
|
330
|
+
results = client.get_reading_results(
|
|
331
|
+
collection_id,
|
|
332
|
+
step["reading_id"],
|
|
333
|
+
include_output=False,
|
|
334
|
+
)
|
|
325
335
|
result_counts[step["alias"]] = len(results)
|
|
326
336
|
except Exception:
|
|
327
337
|
pass
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""Agent run fetch, metadata, transcript groups, and chat sessions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from docent.data_models.agent_run import AgentRun
|
|
8
|
+
from docent.sdk._base import DocentBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DocentAgentRunsMixin(DocentBase):
|
|
12
|
+
"""Agent run and transcript-group operations."""
|
|
13
|
+
|
|
14
|
+
def get_agent_run(self, collection_id: str, agent_run_id: str) -> AgentRun | None:
|
|
15
|
+
"""Get a specific agent run by its ID.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
collection_id: ID of the Collection.
|
|
19
|
+
agent_run_id: The ID of the agent run to retrieve.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
dict: Dictionary containing the agent run information.
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
requests.exceptions.HTTPError: If the API request fails.
|
|
26
|
+
"""
|
|
27
|
+
url = f"{self._api_url}/{collection_id}/agent_run"
|
|
28
|
+
response = self._session.get(url, params={"agent_run_id": agent_run_id})
|
|
29
|
+
self._handle_response_errors(response)
|
|
30
|
+
if response.json() is None:
|
|
31
|
+
return None
|
|
32
|
+
else:
|
|
33
|
+
# We do this to avoid metadata validation failing
|
|
34
|
+
# TODO(mengk): kinda hacky
|
|
35
|
+
return AgentRun.model_validate(response.json())
|
|
36
|
+
|
|
37
|
+
def update_agent_run_metadata(
|
|
38
|
+
self,
|
|
39
|
+
collection_id: str,
|
|
40
|
+
agent_run_id: str,
|
|
41
|
+
metadata: dict[str, Any],
|
|
42
|
+
) -> dict[str, Any]:
|
|
43
|
+
"""Merge metadata into an agent run's existing metadata.
|
|
44
|
+
|
|
45
|
+
Uses a deep merge: nested dictionaries are merged recursively so
|
|
46
|
+
existing keys are preserved, while non-dict values are overwritten.
|
|
47
|
+
Keys not present in ``metadata`` are left unchanged.
|
|
48
|
+
|
|
49
|
+
Requires WRITE permission on the collection.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
collection_id: ID of the Collection containing the agent run.
|
|
53
|
+
agent_run_id: ID of the agent run to update.
|
|
54
|
+
metadata: Dictionary of metadata fields to merge.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The full merged metadata dictionary after the update.
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
requests.exceptions.HTTPError: If the API request fails (e.g., 404 if the agent run is not found).
|
|
61
|
+
"""
|
|
62
|
+
url = f"{self._server_url}/{collection_id}/agent_run/{agent_run_id}/metadata"
|
|
63
|
+
response = self._session.put(url, json={"metadata": metadata})
|
|
64
|
+
self._handle_response_errors(response)
|
|
65
|
+
data: dict[str, Any] = response.json()
|
|
66
|
+
return data
|
|
67
|
+
|
|
68
|
+
def delete_agent_run_metadata_keys(
|
|
69
|
+
self,
|
|
70
|
+
collection_id: str,
|
|
71
|
+
agent_run_id: str,
|
|
72
|
+
keys: list[str],
|
|
73
|
+
) -> tuple[dict[str, Any], list[str]]:
|
|
74
|
+
"""Remove keys from an agent run's metadata.
|
|
75
|
+
|
|
76
|
+
Supports dot-delimited paths for nested deletion. For example,
|
|
77
|
+
``"config.model"`` removes the ``model`` key inside ``config``
|
|
78
|
+
without affecting other keys in that dict.
|
|
79
|
+
|
|
80
|
+
Requires WRITE permission on the collection.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
collection_id: ID of the Collection containing the agent run.
|
|
84
|
+
agent_run_id: ID of the agent run to modify.
|
|
85
|
+
keys: Metadata keys to remove. Use dot-delimited paths for nested
|
|
86
|
+
keys (e.g. ``["top_level_key", "nested.child_key"]``).
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
A tuple of (metadata after deletion, list of keys that were not found).
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
requests.exceptions.HTTPError: If the API request fails (e.g., 404 if the agent run is not found).
|
|
93
|
+
"""
|
|
94
|
+
url = f"{self._server_url}/{collection_id}/agent_run/{agent_run_id}/metadata/delete"
|
|
95
|
+
response = self._session.post(url, json={"keys": keys})
|
|
96
|
+
self._handle_response_errors(response)
|
|
97
|
+
data: dict[str, Any] = response.json()
|
|
98
|
+
metadata: dict[str, Any] = data["metadata"]
|
|
99
|
+
not_found: list[str] = data["not_found"]
|
|
100
|
+
return metadata, not_found
|
|
101
|
+
|
|
102
|
+
def get_agent_run_metadata(
|
|
103
|
+
self,
|
|
104
|
+
collection_id: str,
|
|
105
|
+
agent_run_id: str,
|
|
106
|
+
) -> dict[str, Any]:
|
|
107
|
+
"""Get an agent run's metadata.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
collection_id: ID of the Collection containing the agent run.
|
|
111
|
+
agent_run_id: ID of the agent run.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
The agent run's metadata dict.
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
requests.exceptions.HTTPError: If the API request fails.
|
|
118
|
+
"""
|
|
119
|
+
url = f"{self._server_url}/{collection_id}/agent_run/{agent_run_id}/metadata"
|
|
120
|
+
response = self._session.get(url)
|
|
121
|
+
self._handle_response_errors(response)
|
|
122
|
+
return response.json()
|
|
123
|
+
|
|
124
|
+
# ──────────────────────────────────────────
|
|
125
|
+
# Transcript group metadata
|
|
126
|
+
# ──────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
def get_transcript_group_metadata(
|
|
129
|
+
self,
|
|
130
|
+
collection_id: str,
|
|
131
|
+
transcript_group_id: str,
|
|
132
|
+
) -> dict[str, Any]:
|
|
133
|
+
"""Get a transcript group's metadata.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
collection_id: ID of the Collection containing the transcript group.
|
|
137
|
+
transcript_group_id: ID of the transcript group.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
The transcript group's metadata dict.
|
|
141
|
+
|
|
142
|
+
Raises:
|
|
143
|
+
requests.exceptions.HTTPError: If the API request fails.
|
|
144
|
+
"""
|
|
145
|
+
url = f"{self._server_url}/{collection_id}/transcript_group/{transcript_group_id}/metadata"
|
|
146
|
+
response = self._session.get(url)
|
|
147
|
+
self._handle_response_errors(response)
|
|
148
|
+
return response.json()
|
|
149
|
+
|
|
150
|
+
def update_transcript_group_metadata(
|
|
151
|
+
self,
|
|
152
|
+
collection_id: str,
|
|
153
|
+
transcript_group_id: str,
|
|
154
|
+
metadata: dict[str, Any],
|
|
155
|
+
) -> dict[str, Any]:
|
|
156
|
+
"""Deep-merge metadata into a transcript group's existing metadata.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
collection_id: ID of the Collection containing the transcript group.
|
|
160
|
+
transcript_group_id: ID of the transcript group.
|
|
161
|
+
metadata: Metadata dict to merge into the existing metadata.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
The full merged metadata dict.
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
requests.exceptions.HTTPError: If the API request fails.
|
|
168
|
+
"""
|
|
169
|
+
url = f"{self._server_url}/{collection_id}/transcript_group/{transcript_group_id}/metadata"
|
|
170
|
+
response = self._session.put(url, json={"metadata": metadata})
|
|
171
|
+
self._handle_response_errors(response)
|
|
172
|
+
return response.json()
|
|
173
|
+
|
|
174
|
+
def delete_transcript_group_metadata_keys(
|
|
175
|
+
self,
|
|
176
|
+
collection_id: str,
|
|
177
|
+
transcript_group_id: str,
|
|
178
|
+
keys: list[str],
|
|
179
|
+
) -> tuple[dict[str, Any], list[str]]:
|
|
180
|
+
"""Remove keys from a transcript group's metadata.
|
|
181
|
+
|
|
182
|
+
Supports dot-delimited paths for nested deletion.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
collection_id: ID of the Collection containing the transcript group.
|
|
186
|
+
transcript_group_id: ID of the transcript group.
|
|
187
|
+
keys: Keys to remove. Use dot notation for nested keys.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Tuple of (metadata after deletion, list of keys that were not found).
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
requests.exceptions.HTTPError: If the API request fails.
|
|
194
|
+
"""
|
|
195
|
+
url = f"{self._server_url}/{collection_id}/transcript_group/{transcript_group_id}/metadata/delete"
|
|
196
|
+
response = self._session.post(url, json={"keys": keys})
|
|
197
|
+
self._handle_response_errors(response)
|
|
198
|
+
data = response.json()
|
|
199
|
+
return data["metadata"], data["not_found"]
|
|
200
|
+
|
|
201
|
+
def get_chat_sessions(self, collection_id: str, agent_run_id: str) -> list[dict[str, Any]]:
|
|
202
|
+
"""Get all chat sessions for an agent run, excluding judge result sessions.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
collection_id: ID of the Collection.
|
|
206
|
+
agent_run_id: The ID of the agent run to retrieve chat sessions for.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
list: List of chat session dictionaries.
|
|
210
|
+
|
|
211
|
+
Raises:
|
|
212
|
+
requests.exceptions.HTTPError: If the API request fails.
|
|
213
|
+
"""
|
|
214
|
+
url = f"{self._api_url}/chat/{collection_id}/{agent_run_id}/sessions"
|
|
215
|
+
response = self._session.get(url)
|
|
216
|
+
self._handle_response_errors(response)
|
|
217
|
+
return response.json()
|