keble-task 2.22.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.
- keble_task/__init__.py +143 -0
- keble_task/actions.py +304 -0
- keble_task/agent/__init__.py +27 -0
- keble_task/agent/chat_provider.py +117 -0
- keble_task/agent/deps.py +38 -0
- keble_task/agent/tools/__init__.py +14 -0
- keble_task/agent/tools/mutation.py +100 -0
- keble_task/agent/tools/query.py +160 -0
- keble_task/crud.py +347 -0
- keble_task/exceptions.py +62 -0
- keble_task/main.py +2708 -0
- keble_task/schemas/__init__.py +1295 -0
- keble_task/schemas/for_agent.py +177 -0
- keble_task/task_tree.py +106 -0
- keble_task/utils.py +58 -0
- keble_task-2.22.0.dist-info/METADATA +1083 -0
- keble_task-2.22.0.dist-info/RECORD +18 -0
- keble_task-2.22.0.dist-info/WHEEL +4 -0
keble_task/__init__.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from .crud import CRUDTask, CRUDTaskCost, CRUDTaskRelation
|
|
2
|
+
from .exceptions import (
|
|
3
|
+
TaskException,
|
|
4
|
+
TaskExceptionType,
|
|
5
|
+
TaskFailedToStartException,
|
|
6
|
+
TaskNoSufficientDataException,
|
|
7
|
+
)
|
|
8
|
+
from .actions import (
|
|
9
|
+
CreateRelatedTaskAction,
|
|
10
|
+
CreateRelatedTaskActionedResult,
|
|
11
|
+
TaskAction,
|
|
12
|
+
TaskActionCreatedRelation,
|
|
13
|
+
TaskActionCreatedTask,
|
|
14
|
+
TaskActionEvent,
|
|
15
|
+
TaskActionType,
|
|
16
|
+
TaskActionedResult,
|
|
17
|
+
TaskActionedResults,
|
|
18
|
+
TaskActions,
|
|
19
|
+
TaskEventType,
|
|
20
|
+
TaskLifecycleEvent,
|
|
21
|
+
TaskLifecycleEventPayload,
|
|
22
|
+
TaskUxContext,
|
|
23
|
+
)
|
|
24
|
+
from .main import (
|
|
25
|
+
TaskClient,
|
|
26
|
+
TaskHandlerRequest,
|
|
27
|
+
TaskHandlerResponse,
|
|
28
|
+
TokenConsumptionPayload,
|
|
29
|
+
TokenConsumptionType,
|
|
30
|
+
)
|
|
31
|
+
from .agent import (
|
|
32
|
+
TaskAgentContext,
|
|
33
|
+
TaskAgentDeps,
|
|
34
|
+
TaskAgentMutationToolsConfig,
|
|
35
|
+
TaskAgentQueryToolsConfig,
|
|
36
|
+
TaskQueryChatToolProvider,
|
|
37
|
+
TaskSummaryForAgent,
|
|
38
|
+
register_mutation_tools,
|
|
39
|
+
register_query_tools,
|
|
40
|
+
)
|
|
41
|
+
from .schemas import (
|
|
42
|
+
TaskBase,
|
|
43
|
+
TaskCostAggregateBucket,
|
|
44
|
+
TaskCostAggregateGroupBy,
|
|
45
|
+
TaskCostAggregateRequest,
|
|
46
|
+
TaskCostAggregateResponse,
|
|
47
|
+
TaskCostBase,
|
|
48
|
+
TaskCostCreate,
|
|
49
|
+
TaskCostFilterBase,
|
|
50
|
+
TaskCostListRequest,
|
|
51
|
+
TaskCostListResponse,
|
|
52
|
+
TaskCostMetadata,
|
|
53
|
+
TaskCostMongoObject,
|
|
54
|
+
TaskCostTimeBucket,
|
|
55
|
+
TaskCostTokenRates,
|
|
56
|
+
TaskMongoObject,
|
|
57
|
+
TaskMongoObjectExtended,
|
|
58
|
+
TaskPublicRef,
|
|
59
|
+
TaskRelationMetadata,
|
|
60
|
+
TaskRoomGraphContext,
|
|
61
|
+
TaskRoomGraphRelation,
|
|
62
|
+
TaskRoomGraphTask,
|
|
63
|
+
TaskRoomResolution,
|
|
64
|
+
TaskRelationBase,
|
|
65
|
+
TaskRelationCreate,
|
|
66
|
+
TaskRelationMongoObject,
|
|
67
|
+
TaskRelationType,
|
|
68
|
+
TaskRelationUpdate,
|
|
69
|
+
TaskStage,
|
|
70
|
+
TaskUpdate,
|
|
71
|
+
build_task_cost_metadata,
|
|
72
|
+
build_task_relation_metadata,
|
|
73
|
+
)
|
|
74
|
+
from .utils import Difficulty
|
|
75
|
+
|
|
76
|
+
__all__ = [
|
|
77
|
+
"CRUDTask",
|
|
78
|
+
"CRUDTaskCost",
|
|
79
|
+
"CRUDTaskRelation",
|
|
80
|
+
"Difficulty",
|
|
81
|
+
"TaskBase",
|
|
82
|
+
"TaskCostAggregateBucket",
|
|
83
|
+
"TaskCostAggregateGroupBy",
|
|
84
|
+
"TaskCostAggregateRequest",
|
|
85
|
+
"TaskCostAggregateResponse",
|
|
86
|
+
"TaskCostBase",
|
|
87
|
+
"TaskCostCreate",
|
|
88
|
+
"TaskCostFilterBase",
|
|
89
|
+
"TaskCostListRequest",
|
|
90
|
+
"TaskCostListResponse",
|
|
91
|
+
"TaskCostMetadata",
|
|
92
|
+
"TaskCostMongoObject",
|
|
93
|
+
"TaskCostTimeBucket",
|
|
94
|
+
"TaskCostTokenRates",
|
|
95
|
+
"CreateRelatedTaskAction",
|
|
96
|
+
"CreateRelatedTaskActionedResult",
|
|
97
|
+
"TaskClient",
|
|
98
|
+
"TaskException",
|
|
99
|
+
"TaskExceptionType",
|
|
100
|
+
"TaskFailedToStartException",
|
|
101
|
+
"TaskHandlerRequest",
|
|
102
|
+
"TaskHandlerResponse",
|
|
103
|
+
"TaskMongoObject",
|
|
104
|
+
"TaskMongoObjectExtended",
|
|
105
|
+
"TaskPublicRef",
|
|
106
|
+
"TaskNoSufficientDataException",
|
|
107
|
+
"TaskAction",
|
|
108
|
+
"TaskActionCreatedRelation",
|
|
109
|
+
"TaskActionCreatedTask",
|
|
110
|
+
"TaskAgentContext",
|
|
111
|
+
"TaskAgentDeps",
|
|
112
|
+
"TaskActionEvent",
|
|
113
|
+
"TaskActionType",
|
|
114
|
+
"TaskActionedResult",
|
|
115
|
+
"TaskActionedResults",
|
|
116
|
+
"TaskActions",
|
|
117
|
+
"TaskEventType",
|
|
118
|
+
"TaskLifecycleEvent",
|
|
119
|
+
"TaskLifecycleEventPayload",
|
|
120
|
+
"TaskAgentMutationToolsConfig",
|
|
121
|
+
"TaskAgentQueryToolsConfig",
|
|
122
|
+
"TaskRelationBase",
|
|
123
|
+
"TaskRelationCreate",
|
|
124
|
+
"TaskRelationMetadata",
|
|
125
|
+
"TaskRelationMongoObject",
|
|
126
|
+
"TaskRelationType",
|
|
127
|
+
"TaskRoomGraphContext",
|
|
128
|
+
"TaskRoomGraphRelation",
|
|
129
|
+
"TaskRoomGraphTask",
|
|
130
|
+
"TaskRoomResolution",
|
|
131
|
+
"TaskRelationUpdate",
|
|
132
|
+
"TaskStage",
|
|
133
|
+
"TaskQueryChatToolProvider",
|
|
134
|
+
"TaskSummaryForAgent",
|
|
135
|
+
"TaskUpdate",
|
|
136
|
+
"TaskUxContext",
|
|
137
|
+
"TokenConsumptionPayload",
|
|
138
|
+
"TokenConsumptionType",
|
|
139
|
+
"build_task_cost_metadata",
|
|
140
|
+
"build_task_relation_metadata",
|
|
141
|
+
"register_mutation_tools",
|
|
142
|
+
"register_query_tools",
|
|
143
|
+
]
|
keble_task/actions.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"""Generic task action schemas owned by `keble-task`.
|
|
2
|
+
|
|
3
|
+
The task package owns task creation, parent/root validation, relation rows,
|
|
4
|
+
terminal completion, and action events. Feature packages must expose their own
|
|
5
|
+
typed tools for feature-specific side effects.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Literal
|
|
12
|
+
|
|
13
|
+
from keble_helpers import (
|
|
14
|
+
AgenticActionEvent,
|
|
15
|
+
AgenticActionEventSource,
|
|
16
|
+
AgenticActionEventStatus,
|
|
17
|
+
AgenticActionPayload,
|
|
18
|
+
AgenticActionWarningLevel,
|
|
19
|
+
ObjectId,
|
|
20
|
+
PydanticModelConfig,
|
|
21
|
+
SharingScope,
|
|
22
|
+
)
|
|
23
|
+
from pydantic import BaseModel, Field, field_validator
|
|
24
|
+
|
|
25
|
+
from .schemas import (
|
|
26
|
+
TaskRelationMetadata,
|
|
27
|
+
TaskMongoObject,
|
|
28
|
+
TaskRelationMongoObject,
|
|
29
|
+
TaskRelationType,
|
|
30
|
+
TaskStage,
|
|
31
|
+
build_task_relation_metadata,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
TASK_ACTION_EVENT_SOURCE = AgenticActionEventSource.KEBLE_TASK
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TaskActionType(str, Enum):
|
|
38
|
+
"""Canonical action commands owned by `keble-task`."""
|
|
39
|
+
|
|
40
|
+
CREATE_RELATED_TASK = "CREATE_RELATED_TASK"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TaskEventType(str, Enum):
|
|
44
|
+
"""Canonical non-action task lifecycle events owned by `keble-task`."""
|
|
45
|
+
|
|
46
|
+
TASK_STAGE_CHANGED = "TASK_STAGE_CHANGED"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TaskUxContext(BaseModel):
|
|
50
|
+
"""Optional UI/task selection context for one task action batch.
|
|
51
|
+
|
|
52
|
+
Step by step:
|
|
53
|
+
1. `selected_root_task_id` lets callers prove the visible root workspace;
|
|
54
|
+
2. `selected_task_ids` captures extra user-selected task ids for future action
|
|
55
|
+
types without changing the canonical task model.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
model_config = PydanticModelConfig.default()
|
|
59
|
+
|
|
60
|
+
selected_root_task_id: ObjectId | None = None
|
|
61
|
+
selected_task_ids: list[ObjectId] = Field(default_factory=list)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CreateRelatedTaskAction(AgenticActionPayload):
|
|
65
|
+
"""Create one child task and optional relation edges.
|
|
66
|
+
|
|
67
|
+
This action IS a single `AgenticActionPayload`: it carries its own
|
|
68
|
+
confirmation `message` and `warning_level`, so one approval card maps to
|
|
69
|
+
exactly one action. An agent that wants several related tasks emits several
|
|
70
|
+
`mutate_task_workspace` tool calls (the deferred batch holds them and the
|
|
71
|
+
frontend resolves one card per click) — it must NEVER be re-wrapped in a
|
|
72
|
+
`actions: list[...]` payload that inherits the single-action contract.
|
|
73
|
+
|
|
74
|
+
Step by step:
|
|
75
|
+
1. create a task under `parent_task_id` or the current task by default;
|
|
76
|
+
2. create relation rows from `from_task_ids` or the current task by default;
|
|
77
|
+
3. optionally mark the created task successful for lightweight generic work.
|
|
78
|
+
|
|
79
|
+
Side effect if changes:
|
|
80
|
+
- `keble_task.agent.registry.mutate_task_workspace` binds this as the single
|
|
81
|
+
tool payload; `TaskClient.aapply_action` executes one of these.
|
|
82
|
+
- reused as the element type of the plain REST batch `TaskActions`.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
model_config = PydanticModelConfig.default(extra="forbid")
|
|
86
|
+
|
|
87
|
+
# Defaulted approval copy (not required) so REST/programmatic callers that
|
|
88
|
+
# build this inside the plain `TaskActions` batch need not author per-action
|
|
89
|
+
# confirmation text; an agent may override `message` for a friendlier card.
|
|
90
|
+
message: str = Field(
|
|
91
|
+
default="Create a related task.",
|
|
92
|
+
min_length=1,
|
|
93
|
+
description="Human-friendly confirmation reason for this single action.",
|
|
94
|
+
)
|
|
95
|
+
warning_level: AgenticActionWarningLevel = AgenticActionWarningLevel.MUTATION
|
|
96
|
+
|
|
97
|
+
action_type: Literal[TaskActionType.CREATE_RELATED_TASK] = (
|
|
98
|
+
TaskActionType.CREATE_RELATED_TASK
|
|
99
|
+
)
|
|
100
|
+
relation_type: TaskRelationType
|
|
101
|
+
task_type: str | None = None
|
|
102
|
+
expected_token: int = Field(default=0, ge=0)
|
|
103
|
+
progress_key: str | None = None
|
|
104
|
+
image: str | None = None
|
|
105
|
+
title: str | None = None
|
|
106
|
+
subtitle: str | None = None
|
|
107
|
+
metadata: TaskRelationMetadata = Field(default_factory=dict)
|
|
108
|
+
timeout_mins: int = Field(default=120, ge=1)
|
|
109
|
+
sharing_scope: SharingScope | None = None
|
|
110
|
+
parent_task_id: ObjectId | None = Field(
|
|
111
|
+
default=None,
|
|
112
|
+
description=(
|
|
113
|
+
"Leave unset to attach the new task under the current task (the default). "
|
|
114
|
+
"Only set this to a real task id explicitly provided in the conversation or "
|
|
115
|
+
"context — never invent an id."
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
from_task_ids: list[ObjectId] = Field(
|
|
119
|
+
default_factory=list,
|
|
120
|
+
description=(
|
|
121
|
+
"Leave empty to draw the relation from the current task (the default). Only "
|
|
122
|
+
"include real task ids explicitly provided in context — never invent ids."
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
complete_created_task: bool = False
|
|
126
|
+
consuming_token: int = Field(default=0, ge=0)
|
|
127
|
+
|
|
128
|
+
@field_validator("metadata", mode="before")
|
|
129
|
+
@classmethod
|
|
130
|
+
def _normalize_metadata(cls, value: object) -> TaskRelationMetadata:
|
|
131
|
+
"""Normalize action metadata before it is reused for tasks and relations."""
|
|
132
|
+
|
|
133
|
+
return build_task_relation_metadata(value)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
TaskAction = CreateRelatedTaskAction
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class TaskActionCreatedTask(BaseModel):
|
|
140
|
+
"""Slim public payload for a task created by a task action.
|
|
141
|
+
|
|
142
|
+
Step by step:
|
|
143
|
+
1. expose stable identifiers and display/status fields needed by tools;
|
|
144
|
+
2. omit Mongo-only fields such as metadata, owner, and timestamps;
|
|
145
|
+
3. keep `ObjectId` annotations so `model_dump(mode="json")` emits string ids.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
model_config = PydanticModelConfig.default()
|
|
149
|
+
|
|
150
|
+
task_id: ObjectId
|
|
151
|
+
root_task_id: ObjectId | None = None
|
|
152
|
+
parent_task_id: ObjectId | None = None
|
|
153
|
+
task_type: str
|
|
154
|
+
stage: TaskStage
|
|
155
|
+
title: str | None = None
|
|
156
|
+
progress_key: str | None = None
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def build(cls, *, task: TaskMongoObject) -> "TaskActionCreatedTask":
|
|
160
|
+
"""Convert one internal Mongo task row into the slim action payload."""
|
|
161
|
+
|
|
162
|
+
return cls(
|
|
163
|
+
task_id=task.id,
|
|
164
|
+
root_task_id=task.root_task,
|
|
165
|
+
parent_task_id=task.parent_task,
|
|
166
|
+
task_type=task.task_type,
|
|
167
|
+
stage=task.stage,
|
|
168
|
+
title=task.title,
|
|
169
|
+
progress_key=task.progress_key,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class TaskActionCreatedRelation(BaseModel):
|
|
174
|
+
"""Slim public payload for a relation created by a task action.
|
|
175
|
+
|
|
176
|
+
Step by step:
|
|
177
|
+
1. expose the relation id, root id, endpoints, relation type, and metadata;
|
|
178
|
+
2. omit Mongo timestamps while preserving public relation metadata;
|
|
179
|
+
3. keep `ObjectId` annotations for JSON-mode string serialization.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
model_config = PydanticModelConfig.default()
|
|
183
|
+
|
|
184
|
+
relation_id: ObjectId
|
|
185
|
+
root_task_id: ObjectId
|
|
186
|
+
from_task_id: ObjectId
|
|
187
|
+
to_task_id: ObjectId
|
|
188
|
+
relation_type: TaskRelationType
|
|
189
|
+
metadata: TaskRelationMetadata = Field(default_factory=dict)
|
|
190
|
+
|
|
191
|
+
@field_validator("metadata", mode="before")
|
|
192
|
+
@classmethod
|
|
193
|
+
def _normalize_metadata(cls, value: object) -> TaskRelationMetadata:
|
|
194
|
+
"""Normalize relation event metadata before public JSON-mode dumps."""
|
|
195
|
+
|
|
196
|
+
return build_task_relation_metadata(value)
|
|
197
|
+
|
|
198
|
+
@classmethod
|
|
199
|
+
def build(cls, *, relation: TaskRelationMongoObject) -> "TaskActionCreatedRelation":
|
|
200
|
+
"""Convert one internal Mongo relation row into the slim action payload.
|
|
201
|
+
|
|
202
|
+
Step by step:
|
|
203
|
+
1. copy the stable relation ids and type;
|
|
204
|
+
2. carry the persisted relation metadata through the public event DTO;
|
|
205
|
+
3. leave persistence-only timestamps on the Mongo object.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
return cls(
|
|
209
|
+
relation_id=relation.id,
|
|
210
|
+
root_task_id=relation.root_task,
|
|
211
|
+
from_task_id=relation.from_task_id,
|
|
212
|
+
to_task_id=relation.to_task_id,
|
|
213
|
+
relation_type=relation.relation_type,
|
|
214
|
+
metadata=relation.metadata,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class TaskActions(BaseModel):
|
|
219
|
+
"""Plain ordered batch of canonical task actions for the REST/programmatic seam.
|
|
220
|
+
|
|
221
|
+
This is NOT an `AgenticActionPayload`: a batch cannot honestly carry a single
|
|
222
|
+
approval `message`/`warning_level`. Each element is itself a single
|
|
223
|
+
`AgenticActionPayload` action. The agent surface is one-action-per-tool-call
|
|
224
|
+
(`mutate_task_workspace` + `aapply_action`); only the REST endpoint and
|
|
225
|
+
programmatic callers use this container via `aapply_actions`.
|
|
226
|
+
|
|
227
|
+
Side effect if changes:
|
|
228
|
+
- `keble.backend` `app/api/endpoints/v3/features/tasks.py` REST body.
|
|
229
|
+
- `TaskClient.aapply_actions` iterates `actions` in order.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
model_config = PydanticModelConfig.default(extra="forbid")
|
|
233
|
+
|
|
234
|
+
actions: list[TaskAction] = Field(default_factory=list)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class CreateRelatedTaskActionedResult(BaseModel):
|
|
238
|
+
"""Result for one `CREATE_RELATED_TASK` action using tool-safe DTOs."""
|
|
239
|
+
|
|
240
|
+
model_config = PydanticModelConfig.default()
|
|
241
|
+
|
|
242
|
+
action_type: Literal[TaskActionType.CREATE_RELATED_TASK] = (
|
|
243
|
+
TaskActionType.CREATE_RELATED_TASK
|
|
244
|
+
)
|
|
245
|
+
created_task: TaskActionCreatedTask
|
|
246
|
+
created_relations: list[TaskActionCreatedRelation] = Field(default_factory=list)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
TaskActionedResult = CreateRelatedTaskActionedResult
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class TaskActionEvent(AgenticActionEvent[TaskActionedResult]):
|
|
253
|
+
"""Task action event envelope backed by the shared helper event protocol."""
|
|
254
|
+
|
|
255
|
+
source: AgenticActionEventSource = TASK_ACTION_EVENT_SOURCE
|
|
256
|
+
status: AgenticActionEventStatus = AgenticActionEventStatus.SUCCEEDED
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class TaskLifecycleEventPayload(BaseModel):
|
|
260
|
+
"""Payload for one persisted task lifecycle transition.
|
|
261
|
+
|
|
262
|
+
Step by step:
|
|
263
|
+
1. carry the reloaded persisted task after the stage mutation is committed;
|
|
264
|
+
2. let consumers use the same task schema as task read APIs;
|
|
265
|
+
3. keep lifecycle events separate from task action result payloads.
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
model_config = PydanticModelConfig.default()
|
|
269
|
+
|
|
270
|
+
task: TaskMongoObject
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class TaskLifecycleEvent(AgenticActionEvent[TaskLifecycleEventPayload]):
|
|
274
|
+
"""Task stage-change event envelope emitted by `TaskClient` lifecycle APIs."""
|
|
275
|
+
|
|
276
|
+
source: AgenticActionEventSource = TASK_ACTION_EVENT_SOURCE
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class TaskActionedResults(BaseModel):
|
|
280
|
+
"""Batch result returned by canonical task action execution."""
|
|
281
|
+
|
|
282
|
+
model_config = PydanticModelConfig.default()
|
|
283
|
+
|
|
284
|
+
actioned_results: list[TaskActionedResult] = Field(default_factory=list)
|
|
285
|
+
events: list[TaskActionEvent] = Field(default_factory=list)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
__all__ = [
|
|
289
|
+
"TASK_ACTION_EVENT_SOURCE",
|
|
290
|
+
"CreateRelatedTaskAction",
|
|
291
|
+
"CreateRelatedTaskActionedResult",
|
|
292
|
+
"TaskActionCreatedRelation",
|
|
293
|
+
"TaskActionCreatedTask",
|
|
294
|
+
"TaskAction",
|
|
295
|
+
"TaskActionEvent",
|
|
296
|
+
"TaskActionType",
|
|
297
|
+
"TaskActionedResult",
|
|
298
|
+
"TaskActionedResults",
|
|
299
|
+
"TaskActions",
|
|
300
|
+
"TaskEventType",
|
|
301
|
+
"TaskLifecycleEvent",
|
|
302
|
+
"TaskLifecycleEventPayload",
|
|
303
|
+
"TaskUxContext",
|
|
304
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Public agent-runtime exports for task-owned tool registration.
|
|
2
|
+
|
|
3
|
+
Agent tool I/O schemas + configs are re-exported from
|
|
4
|
+
``keble_task.schemas.for_agent`` for convenience; the registrars live in
|
|
5
|
+
``keble_task.agent.tools`` (symmetric ``mutation``/``query``).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from keble_task.schemas.for_agent import (
|
|
9
|
+
TaskAgentMutationToolsConfig,
|
|
10
|
+
TaskAgentQueryToolsConfig,
|
|
11
|
+
TaskSummaryForAgent,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .chat_provider import TaskQueryChatToolProvider
|
|
15
|
+
from .deps import TaskAgentContext, TaskAgentDeps
|
|
16
|
+
from .tools import register_mutation_tools, register_query_tools
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"TaskAgentContext",
|
|
20
|
+
"TaskAgentDeps",
|
|
21
|
+
"TaskAgentMutationToolsConfig",
|
|
22
|
+
"TaskAgentQueryToolsConfig",
|
|
23
|
+
"TaskQueryChatToolProvider",
|
|
24
|
+
"TaskSummaryForAgent",
|
|
25
|
+
"register_mutation_tools",
|
|
26
|
+
"register_query_tools",
|
|
27
|
+
]
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Native chat tool provider for task-owned READ/QUERY tools.
|
|
2
|
+
|
|
3
|
+
keble.backend used to wrap ``register_query_tools`` in a generic backend-side
|
|
4
|
+
adapter just to satisfy ``keble_helpers.ChatToolProviderProtocol``. This module
|
|
5
|
+
ships the provider natively from the package that OWNS the tools, so any
|
|
6
|
+
agentic-chat host can compose the task query tools declaratively (via
|
|
7
|
+
``compose_tool_providers``-style assembly) without writing its own wrapper.
|
|
8
|
+
|
|
9
|
+
The provider captures its dependencies (``TaskClient`` + optional tool config)
|
|
10
|
+
at construction time; the protocol's ``register(*, agent)`` carries no per-request
|
|
11
|
+
context (deps are construction-captured).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
17
|
+
|
|
18
|
+
from keble_task.main import TaskClient
|
|
19
|
+
|
|
20
|
+
from keble_helpers import (
|
|
21
|
+
ChatToolKind,
|
|
22
|
+
ChatToolProviderId,
|
|
23
|
+
ChatToolProviderManifest,
|
|
24
|
+
AgentToolDescriptor,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from keble_task.schemas.for_agent import TaskAgentQueryToolsConfig
|
|
28
|
+
|
|
29
|
+
from .tools import register_query_tools
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from keble_helpers import ChatToolProviderProtocol
|
|
33
|
+
|
|
34
|
+
__all__ = ["TaskQueryChatToolProvider"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TaskQueryChatToolProvider:
|
|
38
|
+
"""Native ``ChatToolProviderProtocol`` provider for owner-scoped task query tools.
|
|
39
|
+
|
|
40
|
+
One instance contributes the task-owned, non-mutating ``list_tasks`` /
|
|
41
|
+
``get_task`` tools to a single chat-scope agent build. It satisfies the
|
|
42
|
+
framework-neutral ``keble_helpers.ChatToolProviderProtocol``
|
|
43
|
+
(``provider_id`` + ``manifest`` + ``register(*, agent)``) so hosts no longer
|
|
44
|
+
need a generic adapter around ``register_query_tools``.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
*,
|
|
50
|
+
task_client: TaskClient,
|
|
51
|
+
tools_config: TaskAgentQueryToolsConfig | dict[str, Any] | None = None,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Capture the registration dependencies at construction.
|
|
54
|
+
|
|
55
|
+
Step by step:
|
|
56
|
+
1. store the ``TaskClient`` that backs the owner-scoped task reads;
|
|
57
|
+
2. store the optional per-tool name/description override config
|
|
58
|
+
(validated later by ``TaskAgentQueryToolsConfig.build`` inside the
|
|
59
|
+
registrar, so dict payloads are accepted as-is here).
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
self._task_client = task_client
|
|
63
|
+
self._tools_config = tools_config
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def provider_id(self) -> ChatToolProviderId:
|
|
67
|
+
"""Stable provider id recorded in room diagnostics.
|
|
68
|
+
|
|
69
|
+
The ``ChatToolProviderId.TASK_QUERY`` member's wire value
|
|
70
|
+
(``"task_query"``) is what the frontend maps to a user-readable label
|
|
71
|
+
and rooms persist in diagnostics.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
return ChatToolProviderId.TASK_QUERY
|
|
75
|
+
|
|
76
|
+
def register(self, *, agent: Any) -> None:
|
|
77
|
+
"""Attach the task query tools to ``agent`` for one scope build.
|
|
78
|
+
|
|
79
|
+
Run the canonical ``register_query_tools`` registrar with the
|
|
80
|
+
construction-captured client and config, mounting ``list_tasks`` /
|
|
81
|
+
``get_task`` on the agent's tool registry.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
register_query_tools(
|
|
85
|
+
agent,
|
|
86
|
+
task_client=self._task_client,
|
|
87
|
+
tools_config=self._tools_config,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def manifest(self) -> ChatToolProviderManifest:
|
|
92
|
+
"""Declared read-tool inventory (program Phase 6)."""
|
|
93
|
+
|
|
94
|
+
config = TaskAgentQueryToolsConfig.build(self._tools_config)
|
|
95
|
+
return ChatToolProviderManifest(
|
|
96
|
+
provider_id=self.provider_id,
|
|
97
|
+
tools=[
|
|
98
|
+
AgentToolDescriptor(
|
|
99
|
+
name=config.list_tasks.name or "list_tasks",
|
|
100
|
+
kind=ChatToolKind.QUERY,
|
|
101
|
+
summary="List the owner's recent tasks (filterable by type).",
|
|
102
|
+
),
|
|
103
|
+
AgentToolDescriptor(
|
|
104
|
+
name=config.get_task.name or "get_task",
|
|
105
|
+
kind=ChatToolKind.QUERY,
|
|
106
|
+
summary="Read one task by id.",
|
|
107
|
+
),
|
|
108
|
+
],
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if TYPE_CHECKING:
|
|
113
|
+
# Static structural conformance to the cross-repo standard (no runtime cost).
|
|
114
|
+
_conformance: ChatToolProviderProtocol = TaskQueryChatToolProvider(
|
|
115
|
+
task_client=cast(TaskClient, object()),
|
|
116
|
+
)
|
|
117
|
+
_ = _conformance
|
keble_task/agent/deps.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Typed pydantic-ai deps contract for task-owned agent tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from keble_db import AgentDbDeps
|
|
6
|
+
from keble_helpers import AgenticEventEmitter, PydanticModelConfig
|
|
7
|
+
from pydantic import BaseModel, Field, SkipValidation
|
|
8
|
+
|
|
9
|
+
from keble_task.actions import TaskUxContext
|
|
10
|
+
from keble_task.schemas import TaskMongoObject
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TaskAgentContext(BaseModel):
|
|
14
|
+
"""Task-owned runtime context nested under database-wide deps.
|
|
15
|
+
|
|
16
|
+
Step by step this context gives package registrars the runtime pieces that
|
|
17
|
+
cannot live in `AgentDbDeps`:
|
|
18
|
+
1. `current_task` anchors generic task actions;
|
|
19
|
+
2. `ux_context` optionally proves the visible task workspace;
|
|
20
|
+
3. `event_emitter` forwards task action events when the parent runtime has one.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
model_config = PydanticModelConfig.default(arbitrary_types_allowed=True)
|
|
24
|
+
|
|
25
|
+
current_task: TaskMongoObject
|
|
26
|
+
ux_context: TaskUxContext | None = None
|
|
27
|
+
event_emitter: SkipValidation[AgenticEventEmitter | None] = Field(default=None)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TaskAgentDeps(AgentDbDeps):
|
|
31
|
+
"""Database-wide deps plus task-owned runtime state."""
|
|
32
|
+
|
|
33
|
+
model_config = PydanticModelConfig.default(arbitrary_types_allowed=True)
|
|
34
|
+
|
|
35
|
+
task: TaskAgentContext
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
__all__ = ["TaskAgentContext", "TaskAgentDeps"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Task agent tool registrars (behavior only — symmetric mutation/query).
|
|
2
|
+
|
|
3
|
+
Schemas for these tools live in ``keble_task.schemas.for_agent`` (agent tool I/O)
|
|
4
|
+
and the external action module ``keble_task.actions`` (mutation payloads). These
|
|
5
|
+
modules contain ONLY registrars — no contract classes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .mutation import register_mutation_tools
|
|
9
|
+
from .query import register_query_tools
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"register_mutation_tools",
|
|
13
|
+
"register_query_tools",
|
|
14
|
+
]
|