outerproduct-http-types 0.1.0__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.
@@ -0,0 +1,24 @@
1
+ .DS_Store
2
+
3
+ # Python bytecode and caches
4
+ __pycache__/
5
+ *.py[cod]
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .ty/
9
+
10
+ # Python build artifacts
11
+ build/
12
+ dist/
13
+ *.egg-info/
14
+
15
+ # Local virtual environments
16
+ .venv/
17
+
18
+ # Local editor and environment files
19
+ .env
20
+ .env.*
21
+ !.env.example
22
+ .vscode/
23
+ .idea/
24
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OuterProduct
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: outerproduct-http-types
3
+ Version: 0.1.0
4
+ Summary: Shared HTTP-facing type definitions for OuterProduct services and SDKs.
5
+ Project-URL: Homepage, https://outerproduct.com
6
+ Author: OuterProduct, Inc.
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: api,http,outerproduct,pydantic,sdk,wire-types
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Internet :: WWW/HTTP
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.12
22
+ Requires-Dist: pydantic>=2.13.3
23
+ Description-Content-Type: text/markdown
24
+
25
+ # outerproduct-http-types
26
+
27
+ Shared HTTP-facing type definitions for the [OuterProduct](https://outerproduct.com) API.
28
+
29
+ This package contains the [Pydantic](https://docs.pydantic.dev) models that
30
+ describe the request and response shapes used across OuterProduct services and
31
+ SDKs. It is the source of truth for the wire contract between the OuterProduct
32
+ backend and any client (including the official Python SDK).
33
+
34
+ Most users should not depend on this package directly. Instead, install the
35
+ OuterProduct SDK, which re-exports the relevant types and provides a typed
36
+ client. Use this package only when you are building tooling that needs to
37
+ construct or parse OuterProduct API payloads independently of the SDK.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install outerproduct-http-types
43
+ ```
44
+
45
+ Requires Python 3.12 or newer.
46
+
47
+ ## Usage
48
+
49
+ Import the model that matches the endpoint you are working with and construct
50
+ or validate payloads as you would with any Pydantic model:
51
+
52
+ ```python
53
+ from outerproduct_http_types import PredictRequest, PredictResponse
54
+
55
+ request = PredictRequest(
56
+ samples=[[1.0, "x"], [2.5, "y"]],
57
+ feature_names=["feature_a", "feature_b"],
58
+ )
59
+ payload = request.model_dump_json()
60
+
61
+ # ... POST `payload` to /v1/models/{model_id}/predict, then parse:
62
+ response = PredictResponse.model_validate_json(raw_response_body)
63
+ print(response.model_id, response.predictions)
64
+ ```
65
+
66
+ The package ships with a `py.typed` marker, so type checkers (mypy, pyright,
67
+ ty) will pick up the type information automatically.
68
+
69
+ ## Versioning
70
+
71
+ This package follows [semantic versioning](https://semver.org/). Breaking
72
+ changes to wire types are released as a new major version. Minor versions add
73
+ new endpoints or optional fields; patch versions are reserved for documentation
74
+ and non-behavioral fixes.
75
+
76
+ ## License
77
+
78
+ Licensed under the [MIT License](https://opensource.org/license/mit).
@@ -0,0 +1,54 @@
1
+ # outerproduct-http-types
2
+
3
+ Shared HTTP-facing type definitions for the [OuterProduct](https://outerproduct.com) API.
4
+
5
+ This package contains the [Pydantic](https://docs.pydantic.dev) models that
6
+ describe the request and response shapes used across OuterProduct services and
7
+ SDKs. It is the source of truth for the wire contract between the OuterProduct
8
+ backend and any client (including the official Python SDK).
9
+
10
+ Most users should not depend on this package directly. Instead, install the
11
+ OuterProduct SDK, which re-exports the relevant types and provides a typed
12
+ client. Use this package only when you are building tooling that needs to
13
+ construct or parse OuterProduct API payloads independently of the SDK.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install outerproduct-http-types
19
+ ```
20
+
21
+ Requires Python 3.12 or newer.
22
+
23
+ ## Usage
24
+
25
+ Import the model that matches the endpoint you are working with and construct
26
+ or validate payloads as you would with any Pydantic model:
27
+
28
+ ```python
29
+ from outerproduct_http_types import PredictRequest, PredictResponse
30
+
31
+ request = PredictRequest(
32
+ samples=[[1.0, "x"], [2.5, "y"]],
33
+ feature_names=["feature_a", "feature_b"],
34
+ )
35
+ payload = request.model_dump_json()
36
+
37
+ # ... POST `payload` to /v1/models/{model_id}/predict, then parse:
38
+ response = PredictResponse.model_validate_json(raw_response_body)
39
+ print(response.model_id, response.predictions)
40
+ ```
41
+
42
+ The package ships with a `py.typed` marker, so type checkers (mypy, pyright,
43
+ ty) will pick up the type information automatically.
44
+
45
+ ## Versioning
46
+
47
+ This package follows [semantic versioning](https://semver.org/). Breaking
48
+ changes to wire types are released as a new major version. Minor versions add
49
+ new endpoints or optional fields; patch versions are reserved for documentation
50
+ and non-behavioral fixes.
51
+
52
+ ## License
53
+
54
+ Licensed under the [MIT License](https://opensource.org/license/mit).
@@ -0,0 +1,88 @@
1
+ [project]
2
+ name = "outerproduct-http-types"
3
+ version = "0.1.0"
4
+ description = "Shared HTTP-facing type definitions for OuterProduct services and SDKs."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ authors = [
9
+ { name = "OuterProduct, Inc." },
10
+ ]
11
+ keywords = [
12
+ "outerproduct",
13
+ "sdk",
14
+ "http",
15
+ "pydantic",
16
+ "wire-types",
17
+ "api",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 4 - Beta",
21
+ "Intended Audience :: Developers",
22
+ "Operating System :: OS Independent",
23
+ "Programming Language :: Python",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3 :: Only",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Topic :: Software Development :: Libraries :: Python Modules",
29
+ "Topic :: Internet :: WWW/HTTP",
30
+ "Typing :: Typed",
31
+ ]
32
+ license-files = ["LICENSE"]
33
+ dependencies = [
34
+ "pydantic>=2.13.3",
35
+ ]
36
+
37
+ [project.urls]
38
+ Homepage = "https://outerproduct.com"
39
+
40
+ [dependency-groups]
41
+ dev = [
42
+ "prek>=0.3.11",
43
+ "pytest>=8.0",
44
+ "ruff>=0.15.12",
45
+ ]
46
+ lsp = [
47
+ "ty>=0.0.32",
48
+ ]
49
+
50
+ [build-system]
51
+ requires = ["hatchling>=1.27"]
52
+ build-backend = "hatchling.build"
53
+
54
+ [tool.hatch.build.targets.wheel]
55
+ packages = ["src/outerproduct_http_types"]
56
+
57
+ [tool.hatch.build.targets.sdist]
58
+ include = [
59
+ "src/outerproduct_http_types",
60
+ "README.md",
61
+ "LICENSE",
62
+ "pyproject.toml",
63
+ ]
64
+
65
+ [tool.ruff]
66
+ line-length = 100
67
+ target-version = "py312"
68
+ src = ["src"]
69
+ extend-exclude = [".agents"]
70
+ force-exclude = true
71
+
72
+ [tool.ruff.lint]
73
+ select = ["E", "F", "I", "UP", "B", "RUF"]
74
+
75
+ [tool.ruff.lint.isort]
76
+ known-first-party = ["outerproduct_http_types"]
77
+
78
+ [tool.ty.environment]
79
+ python-version = "3.12"
80
+
81
+ [tool.ty.src]
82
+ include = ["src/**/*.py"]
83
+ exclude = [".agents/**"]
84
+
85
+ [tool.ty.terminal]
86
+ output-format = "full"
87
+ error-on-warning = false
88
+
@@ -0,0 +1,117 @@
1
+ """Shared HTTP-facing type definitions for OuterProduct services and SDKs."""
2
+
3
+ from .agentic_documents import (
4
+ AnswerType,
5
+ CreateDocumentUploadRequest,
6
+ CreateDocumentUploadResponse,
7
+ DocumentRef,
8
+ InduceSchemaJobResponse,
9
+ InduceSchemaRequest,
10
+ Question,
11
+ Schema,
12
+ SchemaResultResponse,
13
+ TabularizeJobResponse,
14
+ TabularizeRequest,
15
+ TabularizeResultResponse,
16
+ )
17
+ from .common import (
18
+ ErrorResponse,
19
+ FeatureImportance,
20
+ JobResponse,
21
+ JobStatus,
22
+ StatusResponse,
23
+ )
24
+ from .connectors import (
25
+ ConnectorResponse,
26
+ ConnectorType,
27
+ CreateConnectorRequest,
28
+ DeleteConnectorResponse,
29
+ ListConnectorsResponse,
30
+ ListTablesRequest,
31
+ ListTablesResponse,
32
+ ValidateConnectorResponse,
33
+ )
34
+ from .inference import (
35
+ ExplainRequest,
36
+ ExplainResponse,
37
+ FeatureConstraintSchema,
38
+ InterpretRequest,
39
+ InterpretResponse,
40
+ PredictAndExplainRequest,
41
+ PredictAndExplainResponse,
42
+ PredictRequest,
43
+ PredictResponse,
44
+ ScenarioCandidate,
45
+ ScenarioChange,
46
+ ScenarioRequest,
47
+ ScenarioResponse,
48
+ ScenarioResultItem,
49
+ )
50
+ from .reasoning import ReasoningFitRequest, ReasoningFitResponse
51
+ from .segment import ClusterPersonaSchema, SegmentRequest, SegmentResultResponse
52
+ from .trainer import (
53
+ HardwareSpec,
54
+ ModalHardwareSpec,
55
+ TrainerRunRequest,
56
+ TrainerRunResponse,
57
+ )
58
+ from .uploads import CreateUploadRequest, CreateUploadResponse
59
+ from .workers import WorkerJobRequest, WorkerJobResponse
60
+
61
+ __version__ = "0.1.0"
62
+
63
+ __all__ = [
64
+ "AnswerType",
65
+ "ClusterPersonaSchema",
66
+ "ConnectorResponse",
67
+ "ConnectorType",
68
+ "CreateConnectorRequest",
69
+ "CreateDocumentUploadRequest",
70
+ "CreateDocumentUploadResponse",
71
+ "CreateUploadRequest",
72
+ "CreateUploadResponse",
73
+ "DeleteConnectorResponse",
74
+ "DocumentRef",
75
+ "ErrorResponse",
76
+ "ExplainRequest",
77
+ "ExplainResponse",
78
+ "FeatureConstraintSchema",
79
+ "FeatureImportance",
80
+ "HardwareSpec",
81
+ "InduceSchemaJobResponse",
82
+ "InduceSchemaRequest",
83
+ "InterpretRequest",
84
+ "InterpretResponse",
85
+ "JobResponse",
86
+ "JobStatus",
87
+ "ListConnectorsResponse",
88
+ "ListTablesRequest",
89
+ "ListTablesResponse",
90
+ "ModalHardwareSpec",
91
+ "PredictAndExplainRequest",
92
+ "PredictAndExplainResponse",
93
+ "PredictRequest",
94
+ "PredictResponse",
95
+ "Question",
96
+ "ReasoningFitRequest",
97
+ "ReasoningFitResponse",
98
+ "ScenarioCandidate",
99
+ "ScenarioChange",
100
+ "ScenarioRequest",
101
+ "ScenarioResponse",
102
+ "ScenarioResultItem",
103
+ "Schema",
104
+ "SchemaResultResponse",
105
+ "SegmentRequest",
106
+ "SegmentResultResponse",
107
+ "StatusResponse",
108
+ "TabularizeJobResponse",
109
+ "TabularizeRequest",
110
+ "TabularizeResultResponse",
111
+ "TrainerRunRequest",
112
+ "TrainerRunResponse",
113
+ "ValidateConnectorResponse",
114
+ "WorkerJobRequest",
115
+ "WorkerJobResponse",
116
+ "__version__",
117
+ ]
@@ -0,0 +1,239 @@
1
+ """Request/response schemas for the agentic-documents endpoints.
2
+
3
+ Wire format for ``outerproduct.agentic.documents``: per-file presigned
4
+ uploads, async ``induce_schema`` and ``tabularize`` jobs polled via the
5
+ shared ``GET /v1/models/{model_id}/status`` route, and result fetches
6
+ keyed by the same ``model_id``.
7
+
8
+ Endpoint summary:
9
+
10
+ - ``POST /v1/uploads/documents`` -> :class:`CreateDocumentUploadResponse`
11
+ - ``POST /v1/agentic/documents/induce_schema`` -> :class:`InduceSchemaJobResponse`
12
+ - ``GET /v1/agentic/documents/schemas/{model_id}`` -> :class:`SchemaResultResponse`
13
+ - ``POST /v1/agentic/documents/tabularize`` -> :class:`TabularizeJobResponse`
14
+ - ``GET /v1/agentic/documents/tables/{model_id}`` -> :class:`TabularizeResultResponse`
15
+ """
16
+
17
+ from enum import StrEnum
18
+ from typing import Any, Literal
19
+
20
+ from pydantic import BaseModel, Field, model_validator
21
+
22
+ from .common import JobResponse
23
+
24
+
25
+ class AnswerType(StrEnum):
26
+ """The legal answer shapes for a :class:`Question`."""
27
+
28
+ BOOLEAN = "boolean"
29
+ NUMBER = "number"
30
+ INTEGER = "integer"
31
+ ENUM = "enum"
32
+ MULTI_ENUM = "multi_enum"
33
+ DATE = "date"
34
+ DATE_RANGE = "date_range"
35
+ STRING = "string"
36
+ TEXT = "text"
37
+
38
+
39
+ _DOCUMENT_MEDIA_TYPES = Literal[
40
+ "application/pdf",
41
+ "image/png",
42
+ "image/jpeg",
43
+ "image/gif",
44
+ "image/webp",
45
+ "text/plain",
46
+ ]
47
+
48
+
49
+ class Question(BaseModel):
50
+ """One survey question in a :class:`Schema`."""
51
+
52
+ id: str = Field(
53
+ ...,
54
+ description="Snake-case identifier, unique within the schema.",
55
+ pattern=r"^[a-z][a-z0-9_]*$",
56
+ )
57
+ question: str = Field(..., description="Natural-language prompt the agent answers.")
58
+ answer_type: AnswerType
59
+ rationale: str | None = Field(
60
+ None, description="Why this question is on the schema; surfaced to the agent."
61
+ )
62
+ unit: str | None = Field(
63
+ None, description="Unit string for numeric answers (e.g. 'USD', 'kg')."
64
+ )
65
+ enum: list[str] | None = Field(
66
+ None,
67
+ description="Allowed values when answer_type is 'enum' or 'multi_enum'.",
68
+ )
69
+
70
+ @model_validator(mode="after")
71
+ def _check_enum(self):
72
+ if self.answer_type in (AnswerType.ENUM, AnswerType.MULTI_ENUM):
73
+ if not self.enum:
74
+ raise ValueError(
75
+ f"Question {self.id!r}: answer_type={self.answer_type.value} "
76
+ "requires non-empty `enum`"
77
+ )
78
+ elif self.enum is not None:
79
+ raise ValueError(
80
+ f"Question {self.id!r}: `enum` is only valid for "
81
+ "answer_type='enum' or 'multi_enum'"
82
+ )
83
+ return self
84
+
85
+
86
+ class Schema(BaseModel):
87
+ """A frozen list of :class:`Question` for one document type and one use case."""
88
+
89
+ skill: str = Field(..., description="Skill slug (e.g. 'invoice', 'bank-statement').")
90
+ use_case: str = Field(..., description="Use-case description that drove induction.")
91
+ questions: list[Question]
92
+ metadata: dict[str, Any] = Field(default_factory=dict)
93
+
94
+ @model_validator(mode="after")
95
+ def _check_unique_ids(self):
96
+ seen: set[str] = set()
97
+ for q in self.questions:
98
+ if q.id in seen:
99
+ raise ValueError(f"Schema: duplicate question id {q.id!r}")
100
+ seen.add(q.id)
101
+ return self
102
+
103
+
104
+ # --------------------------------------------------------------------------- #
105
+ # Per-document upload #
106
+ # --------------------------------------------------------------------------- #
107
+
108
+
109
+ class CreateDocumentUploadRequest(BaseModel):
110
+ """``POST /v1/uploads/documents`` -- request a presigned URL for one file."""
111
+
112
+ document_id: str = Field(
113
+ ...,
114
+ description="Caller-chosen identifier; echoed back in subsequent requests.",
115
+ )
116
+ media_type: _DOCUMENT_MEDIA_TYPES = Field(
117
+ ...,
118
+ description="MIME type of the bytes you will PUT to the returned URL.",
119
+ )
120
+
121
+
122
+ class CreateDocumentUploadResponse(BaseModel):
123
+ document_id: str
124
+ upload_url: str
125
+ upload_key: str = Field(
126
+ ...,
127
+ description="Reference to use as DocumentRef.upload_key in induce/tabularize.",
128
+ )
129
+ media_type: _DOCUMENT_MEDIA_TYPES
130
+ expires_in: int = Field(..., description="Seconds until the upload URL expires.")
131
+
132
+
133
+ class DocumentRef(BaseModel):
134
+ """One uploaded document, by reference."""
135
+
136
+ document_id: str
137
+ upload_key: str
138
+ media_type: _DOCUMENT_MEDIA_TYPES
139
+
140
+
141
+ # --------------------------------------------------------------------------- #
142
+ # induce_schema #
143
+ # --------------------------------------------------------------------------- #
144
+
145
+
146
+ class InduceSchemaRequest(BaseModel):
147
+ """``POST /v1/agentic/documents/induce_schema`` -- async schema induction job.
148
+
149
+ Runs the agent over the supplied document sample to produce a frozen
150
+ :class:`Schema`. Submit, poll ``GET /v1/models/{model_id}/status`` until
151
+ ``status="completed"``, then fetch the result via
152
+ ``GET /v1/agentic/documents/schemas/{model_id}``.
153
+ """
154
+
155
+ documents: list[DocumentRef] = Field(
156
+ ...,
157
+ description="Sample of uploaded documents the agent inspects to draft the schema.",
158
+ min_length=1,
159
+ )
160
+ use_case: str = Field(
161
+ ...,
162
+ description="Free-text description of what the resulting features will be used for.",
163
+ )
164
+ skill: str = Field(
165
+ ...,
166
+ description="Skill slug; one of the values returned by GET /v1/agentic/documents/skills.",
167
+ )
168
+ model_id: str | None = Field(
169
+ None,
170
+ description="Custom model_id for the resulting schema; auto-generated if omitted.",
171
+ )
172
+
173
+
174
+ class InduceSchemaJobResponse(JobResponse):
175
+ """``POST /v1/agentic/documents/induce_schema`` -- async submission response."""
176
+
177
+
178
+ class SchemaResultResponse(BaseModel):
179
+ """``GET /v1/agentic/documents/schemas/{model_id}`` -- the produced schema."""
180
+
181
+ model_id: str
182
+ schema_: Schema = Field(..., alias="schema")
183
+
184
+ model_config = {"populate_by_name": True}
185
+
186
+
187
+ # --------------------------------------------------------------------------- #
188
+ # tabularize #
189
+ # --------------------------------------------------------------------------- #
190
+
191
+
192
+ class TabularizeRequest(BaseModel):
193
+ """``POST /v1/agentic/documents/tabularize`` -- async tabularization job.
194
+
195
+ Extracts every uploaded document against ``schema`` and assembles a row
196
+ per document. Submit, poll status, then fetch the result via
197
+ ``GET /v1/agentic/documents/tables/{model_id}``.
198
+ """
199
+
200
+ documents: list[DocumentRef] = Field(..., min_length=1)
201
+ schema_: Schema = Field(..., alias="schema")
202
+ web_augmentation: bool = Field(
203
+ False,
204
+ description="If true, allow the agent to issue web searches to corroborate answers.",
205
+ )
206
+ concurrency: int = Field(
207
+ 1,
208
+ ge=1,
209
+ description="Number of documents to extract in parallel on the worker.",
210
+ )
211
+ model_id: str | None = Field(
212
+ None,
213
+ description="Custom model_id for the resulting table; auto-generated if omitted.",
214
+ )
215
+
216
+ model_config = {"populate_by_name": True}
217
+
218
+
219
+ class TabularizeJobResponse(JobResponse):
220
+ """``POST /v1/agentic/documents/tabularize`` -- async submission response."""
221
+
222
+
223
+ class TabularizeResultResponse(BaseModel):
224
+ """``GET /v1/agentic/documents/tables/{model_id}`` -- the tabularized result.
225
+
226
+ The tabularized rows themselves live in object storage under the
227
+ returned ``model_id`` (same path used by trainer.run / reasoning.fit
228
+ in their pre-uploaded mode), so this response is metadata-only:
229
+ enough to construct a :class:`outerproduct.agentic.documents.DocumentDataset`
230
+ handle without materializing rows on the client.
231
+ """
232
+
233
+ model_id: str
234
+ schema_: Schema = Field(..., alias="schema")
235
+ document_ids: list[str]
236
+
237
+ model_config = {"populate_by_name": True}
238
+
239
+
@@ -0,0 +1,49 @@
1
+ """Shared types used across all endpoint schemas."""
2
+
3
+ from datetime import datetime
4
+ from enum import StrEnum
5
+ from typing import Any
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class JobStatus(StrEnum):
11
+ PENDING = "pending"
12
+ RUNNING = "running"
13
+ COMPLETED = "completed"
14
+ FAILED = "failed"
15
+
16
+
17
+ class ErrorResponse(BaseModel):
18
+ error: str
19
+ detail: str | None = None
20
+
21
+
22
+ class JobResponse(BaseModel):
23
+ """Base response returned by async job-submission endpoints."""
24
+
25
+ model_id: str = Field(description="Unique identifier for this model")
26
+ status: JobStatus
27
+ message: str
28
+
29
+
30
+ class StatusResponse(BaseModel):
31
+ """Returned by GET /models/{model_id}/status."""
32
+
33
+ model_id: str
34
+ job_type: str = Field(description=("One of: trainer_run, reasoning_fit, segment."))
35
+ status: JobStatus
36
+ progress: dict[str, Any] | None = Field(
37
+ None, description='Progress info, e.g. {"step": 3, "total_steps": 5}'
38
+ )
39
+ error_message: str | None = None
40
+ created_at: datetime
41
+ updated_at: datetime
42
+
43
+
44
+ class FeatureImportance(BaseModel):
45
+ """A single feature's importance score with direction."""
46
+
47
+ feature_name: str
48
+ importance: float
49
+ direction: str | None = Field(None, description='"positive" or "negative"')