goose-py 0.11.25__py3-none-any.whl → 0.12.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.
- goose/__init__.py +15 -0
- goose/_internal/agent.py +114 -4
- goose/_internal/flow.py +126 -0
- goose/_internal/result.py +36 -0
- goose/_internal/task.py +161 -0
- goose/errors.py +14 -0
- goose_py-0.12.0.dist-info/METADATA +248 -0
- {goose_py-0.11.25.dist-info → goose_py-0.12.0.dist-info}/RECORD +9 -9
- goose_py-0.11.25.dist-info/METADATA +0 -14
- {goose_py-0.11.25.dist-info → goose_py-0.12.0.dist-info}/WHEEL +0 -0
goose/__init__.py
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
"""Goose: A framework for building LLM-based agents and workflows.
|
2
|
+
|
3
|
+
Goose provides tools for creating structured agent applications with support for:
|
4
|
+
- Task-based workflow orchestration
|
5
|
+
- Caching and state management
|
6
|
+
- Result validation and typing
|
7
|
+
- Agent conversations and refinement
|
8
|
+
|
9
|
+
Main components:
|
10
|
+
- Agent: Base agent for interacting with LLMs
|
11
|
+
- flow: Decorator for creating connected workflows
|
12
|
+
- task: Decorator for creating individual tasks
|
13
|
+
- Result: Base class for structured response types
|
14
|
+
"""
|
15
|
+
|
1
16
|
from goose._internal.agent import Agent
|
2
17
|
from goose._internal.flow import FlowArguments, flow
|
3
18
|
from goose._internal.result import Result, TextResult
|
goose/_internal/agent.py
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
"""Agent module for interacting with language models.
|
2
|
+
|
3
|
+
This module provides the Agent class for handling interactions with language models,
|
4
|
+
along with protocols for custom logging.
|
5
|
+
"""
|
6
|
+
|
1
7
|
import logging
|
2
8
|
from datetime import datetime
|
3
9
|
from typing import Any, Literal, Protocol, overload
|
@@ -22,10 +28,27 @@ ExpectedMessage = LLMUserMessage | LLMAssistantMessage | LLMSystemMessage | LLMT
|
|
22
28
|
|
23
29
|
|
24
30
|
class IAgentLogger(Protocol):
|
31
|
+
"""Protocol for custom agent response logging.
|
32
|
+
|
33
|
+
Implement this protocol to create custom loggers for agent responses.
|
34
|
+
"""
|
35
|
+
|
25
36
|
async def __call__(self, *, response: AgentResponse[Any]) -> None: ...
|
26
37
|
|
27
38
|
|
28
39
|
class Agent:
|
40
|
+
"""Agent for interacting with language models.
|
41
|
+
|
42
|
+
The Agent class handles interactions with language models, including generating
|
43
|
+
structured and unstructured responses, asking questions, and refining results.
|
44
|
+
It also manages logging of model interactions.
|
45
|
+
|
46
|
+
Attributes:
|
47
|
+
flow_name: The name of the flow this agent is part of
|
48
|
+
run_id: The ID of the current run
|
49
|
+
logger: Optional custom logger for agent responses
|
50
|
+
"""
|
51
|
+
|
29
52
|
def __init__(
|
30
53
|
self,
|
31
54
|
*,
|
@@ -33,6 +56,13 @@ class Agent:
|
|
33
56
|
run_id: str,
|
34
57
|
logger: IAgentLogger | None = None,
|
35
58
|
) -> None:
|
59
|
+
"""Initialize an Agent.
|
60
|
+
|
61
|
+
Args:
|
62
|
+
flow_name: The name of the flow this agent is part of
|
63
|
+
run_id: The ID of the current run
|
64
|
+
logger: Optional custom logger for agent responses
|
65
|
+
"""
|
36
66
|
self.flow_name = flow_name
|
37
67
|
self.run_id = run_id
|
38
68
|
self.logger = logger
|
@@ -46,15 +76,33 @@ class Agent:
|
|
46
76
|
router: LLMRouter[M],
|
47
77
|
response_model: type[R] = TextResult,
|
48
78
|
) -> R:
|
79
|
+
"""Generate a structured response from the language model.
|
80
|
+
|
81
|
+
This method sends a sequence of messages to the language model and expects
|
82
|
+
a structured response conforming to the provided response_model.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
messages: List of messages to send to the language model
|
86
|
+
model: The language model alias to use
|
87
|
+
task_name: Name of the task for logging and tracking
|
88
|
+
router: LLM router for routing the request
|
89
|
+
response_model: Pydantic model class for the expected response structure
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
A validated instance of the response_model
|
93
|
+
|
94
|
+
Raises:
|
95
|
+
ValidationError: If the response cannot be parsed into the response_model
|
96
|
+
"""
|
49
97
|
start_time = datetime.now()
|
50
98
|
typed_messages: list[ExpectedMessage] = [*messages]
|
51
99
|
|
52
100
|
if response_model is TextResult:
|
53
|
-
response = await llm_unstructured(
|
101
|
+
response = await llm_unstructured(messages=typed_messages, router=router)
|
54
102
|
parsed_response = response_model.model_validate({"text": response.text})
|
55
103
|
else:
|
56
104
|
response = await llm_structured(
|
57
|
-
|
105
|
+
messages=typed_messages, response_model=response_model, router=router
|
58
106
|
)
|
59
107
|
parsed_response = response.structured_response
|
60
108
|
|
@@ -96,9 +144,23 @@ class Agent:
|
|
96
144
|
task_name: str,
|
97
145
|
router: LLMRouter[M],
|
98
146
|
) -> str:
|
147
|
+
"""Ask the language model for an unstructured text response.
|
148
|
+
|
149
|
+
This method sends a sequence of messages to the language model and
|
150
|
+
receives a free-form text response.
|
151
|
+
|
152
|
+
Args:
|
153
|
+
messages: List of messages to send to the language model
|
154
|
+
model: The language model alias to use
|
155
|
+
task_name: Name of the task for logging and tracking
|
156
|
+
router: LLM router for routing the request
|
157
|
+
|
158
|
+
Returns:
|
159
|
+
The text response from the language model
|
160
|
+
"""
|
99
161
|
start_time = datetime.now()
|
100
162
|
typed_messages: list[ExpectedMessage] = [*messages]
|
101
|
-
response = await llm_unstructured(
|
163
|
+
response = await llm_unstructured(messages=typed_messages, router=router)
|
102
164
|
end_time = datetime.now()
|
103
165
|
|
104
166
|
if isinstance(messages[0], LLMSystemMessage):
|
@@ -138,10 +200,30 @@ class Agent:
|
|
138
200
|
task_name: str,
|
139
201
|
response_model: type[R],
|
140
202
|
) -> R:
|
203
|
+
"""Refine a previous structured response based on feedback.
|
204
|
+
|
205
|
+
This method uses a find-and-replace approach to refine a previous structured
|
206
|
+
response. It identifies parts of the previous response to change and applies
|
207
|
+
these changes to create an updated response.
|
208
|
+
|
209
|
+
Args:
|
210
|
+
messages: List of messages including the previous response and feedback
|
211
|
+
model: The language model alias to use
|
212
|
+
router: LLM router for routing the request
|
213
|
+
task_name: Name of the task for logging and tracking
|
214
|
+
response_model: The model class of the response to refine
|
215
|
+
|
216
|
+
Returns:
|
217
|
+
A refined instance of the response_model
|
218
|
+
|
219
|
+
Raises:
|
220
|
+
Honk: If no previous result is found in the message history
|
221
|
+
ValidationError: If the refined result cannot be validated against the response_model
|
222
|
+
"""
|
141
223
|
start_time = datetime.now()
|
142
224
|
typed_messages: list[ExpectedMessage] = [*messages]
|
143
225
|
find_replace_response = await llm_structured(
|
144
|
-
|
226
|
+
messages=typed_messages, response_model=FindReplaceResponse, router=router
|
145
227
|
)
|
146
228
|
parsed_find_replace_response = find_replace_response.structured_response
|
147
229
|
end_time = datetime.now()
|
@@ -252,6 +334,19 @@ class Agent:
|
|
252
334
|
def __apply_find_replace[R: Result](
|
253
335
|
self, *, result: R, find_replace_response: FindReplaceResponse, response_model: type[R]
|
254
336
|
) -> R:
|
337
|
+
"""Apply find-replace operations to a result.
|
338
|
+
|
339
|
+
Takes a result object and a set of replacements, applies the replacements,
|
340
|
+
and validates the new result against the response model.
|
341
|
+
|
342
|
+
Args:
|
343
|
+
result: The original result to modify
|
344
|
+
find_replace_response: Object containing the replacements to apply
|
345
|
+
response_model: The model class to validate the result against
|
346
|
+
|
347
|
+
Returns:
|
348
|
+
A new instance of the response model with replacements applied
|
349
|
+
"""
|
255
350
|
dumped_result = result.model_dump_json()
|
256
351
|
for replacement in find_replace_response.replacements:
|
257
352
|
dumped_result = dumped_result.replace(replacement.find, replacement.replace)
|
@@ -261,6 +356,21 @@ class Agent:
|
|
261
356
|
def __find_last_result[R: Result](
|
262
357
|
self, *, messages: list[LLMUserMessage | LLMAssistantMessage | LLMSystemMessage], response_model: type[R]
|
263
358
|
) -> R:
|
359
|
+
"""Find the last result in a conversation history.
|
360
|
+
|
361
|
+
Searches through messages in reverse order to find the most recent
|
362
|
+
assistant message that can be parsed as the given response model.
|
363
|
+
|
364
|
+
Args:
|
365
|
+
messages: List of messages to search through
|
366
|
+
response_model: The model class to validate found results against
|
367
|
+
|
368
|
+
Returns:
|
369
|
+
The last result that can be validated as the response model
|
370
|
+
|
371
|
+
Raises:
|
372
|
+
Honk: If no valid result is found in the message history
|
373
|
+
"""
|
264
374
|
for message in reversed(messages):
|
265
375
|
if isinstance(message, LLMAssistantMessage):
|
266
376
|
try:
|
goose/_internal/flow.py
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
"""Flow module for orchestrating sequences of tasks.
|
2
|
+
|
3
|
+
This module provides the Flow class and flow decorator for building
|
4
|
+
workflows that coordinate multiple tasks with state management,
|
5
|
+
persistence, and run tracking.
|
6
|
+
"""
|
7
|
+
|
1
8
|
from collections.abc import AsyncIterator, Callable
|
2
9
|
from contextlib import asynccontextmanager
|
3
10
|
from types import CodeType
|
@@ -12,18 +19,46 @@ from .store import IFlowRunStore, InMemoryFlowRunStore
|
|
12
19
|
|
13
20
|
|
14
21
|
class IGenerator[FlowArgumentsT: FlowArguments](Protocol):
|
22
|
+
"""Protocol for flow generator functions.
|
23
|
+
|
24
|
+
This protocol defines the interface for functions that can be used
|
25
|
+
to create flows.
|
26
|
+
|
27
|
+
Type Parameters:
|
28
|
+
FlowArgumentsT: Type of flow arguments
|
29
|
+
"""
|
30
|
+
|
15
31
|
__name__: str
|
16
32
|
|
17
33
|
async def __call__(self, *, flow_arguments: FlowArgumentsT, agent: Agent) -> None: ...
|
18
34
|
|
19
35
|
|
20
36
|
class IAdapter[ResultT: Result](Protocol):
|
37
|
+
"""Protocol for adapters that transform conversations to results.
|
38
|
+
|
39
|
+
This protocol defines the interface for functions that can adapt
|
40
|
+
conversations to structured results.
|
41
|
+
|
42
|
+
Type Parameters:
|
43
|
+
ResultT: Type of result
|
44
|
+
"""
|
45
|
+
|
21
46
|
__code__: CodeType
|
22
47
|
|
23
48
|
async def __call__(self, *, conversation: Conversation, agent: Agent) -> ResultT: ...
|
24
49
|
|
25
50
|
|
26
51
|
class Flow[FlowArgumentsT: FlowArguments]:
|
52
|
+
"""Orchestrates a sequence of tasks with persistent state.
|
53
|
+
|
54
|
+
A Flow manages the execution of tasks, tracks state, and handles
|
55
|
+
persistence of results. It provides mechanisms for starting runs,
|
56
|
+
generating results, and regenerating flows.
|
57
|
+
|
58
|
+
Type Parameters:
|
59
|
+
FlowArgumentsT: Type of flow arguments
|
60
|
+
"""
|
61
|
+
|
27
62
|
def __init__(
|
28
63
|
self,
|
29
64
|
fn: IGenerator[FlowArgumentsT],
|
@@ -33,6 +68,14 @@ class Flow[FlowArgumentsT: FlowArguments]:
|
|
33
68
|
store: IFlowRunStore | None = None,
|
34
69
|
agent_logger: IAgentLogger | None = None,
|
35
70
|
) -> None:
|
71
|
+
"""Initialize a Flow.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
fn: The flow generator function
|
75
|
+
name: Optional custom name for the flow (defaults to function name)
|
76
|
+
store: Optional store for persisting flow runs
|
77
|
+
agent_logger: Optional logger for agent responses
|
78
|
+
"""
|
36
79
|
self._fn = fn
|
37
80
|
self._name = name
|
38
81
|
self._agent_logger = agent_logger
|
@@ -40,6 +83,14 @@ class Flow[FlowArgumentsT: FlowArguments]:
|
|
40
83
|
|
41
84
|
@property
|
42
85
|
def flow_arguments_model(self) -> type[FlowArgumentsT]:
|
86
|
+
"""Get the flow arguments model type.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
The FlowArguments type used by this flow
|
90
|
+
|
91
|
+
Raises:
|
92
|
+
Honk: If the flow function has an invalid signature
|
93
|
+
"""
|
43
94
|
arguments_model = self._fn.__annotations__.get("flow_arguments")
|
44
95
|
if arguments_model is None:
|
45
96
|
raise Honk("Flow function has an invalid signature. Must accept `flow_arguments` and `agent` as arguments.")
|
@@ -48,10 +99,23 @@ class Flow[FlowArgumentsT: FlowArguments]:
|
|
48
99
|
|
49
100
|
@property
|
50
101
|
def name(self) -> str:
|
102
|
+
"""Get the name of the flow.
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
The name of the flow (custom name or function name)
|
106
|
+
"""
|
51
107
|
return self._name or self._fn.__name__
|
52
108
|
|
53
109
|
@property
|
54
110
|
def current_run(self) -> FlowRun[FlowArgumentsT]:
|
111
|
+
"""Get the current flow run.
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
The current flow run
|
115
|
+
|
116
|
+
Raises:
|
117
|
+
Honk: If there is no current flow run
|
118
|
+
"""
|
55
119
|
run = get_current_flow_run()
|
56
120
|
if run is None:
|
57
121
|
raise Honk("No current flow run")
|
@@ -59,6 +123,23 @@ class Flow[FlowArgumentsT: FlowArguments]:
|
|
59
123
|
|
60
124
|
@asynccontextmanager
|
61
125
|
async def start_run(self, *, run_id: str) -> AsyncIterator[FlowRun[FlowArgumentsT]]:
|
126
|
+
"""Start a new run of this flow.
|
127
|
+
|
128
|
+
This context manager starts a new flow run or loads an existing one,
|
129
|
+
sets it as the current run, and handles persistence when the context exits.
|
130
|
+
|
131
|
+
Args:
|
132
|
+
run_id: Unique identifier for this run
|
133
|
+
|
134
|
+
Yields:
|
135
|
+
The flow run instance
|
136
|
+
|
137
|
+
Example:
|
138
|
+
```python
|
139
|
+
async with my_flow.start_run(run_id="123") as run:
|
140
|
+
await my_flow.generate(MyFlowArguments(...))
|
141
|
+
```
|
142
|
+
"""
|
62
143
|
existing_serialized_run = await self._store.get(run_id=run_id)
|
63
144
|
if existing_serialized_run is not None:
|
64
145
|
run = FlowRun.load(
|
@@ -78,6 +159,17 @@ class Flow[FlowArgumentsT: FlowArguments]:
|
|
78
159
|
set_current_flow_run(old_run)
|
79
160
|
|
80
161
|
async def generate(self, flow_arguments: FlowArgumentsT, /) -> None:
|
162
|
+
"""Execute the flow with the given arguments.
|
163
|
+
|
164
|
+
This method runs the flow function with the provided arguments,
|
165
|
+
executing all tasks defined within the flow.
|
166
|
+
|
167
|
+
Args:
|
168
|
+
flow_arguments: Arguments for the flow
|
169
|
+
|
170
|
+
Raises:
|
171
|
+
Honk: If there is no current flow run
|
172
|
+
"""
|
81
173
|
flow_run = get_current_flow_run()
|
82
174
|
if flow_run is None:
|
83
175
|
raise Honk("No current flow run")
|
@@ -86,6 +178,14 @@ class Flow[FlowArgumentsT: FlowArguments]:
|
|
86
178
|
await self._fn(flow_arguments=flow_arguments, agent=flow_run.agent)
|
87
179
|
|
88
180
|
async def regenerate(self) -> None:
|
181
|
+
"""Regenerate the flow using the same arguments.
|
182
|
+
|
183
|
+
This method re-executes the flow function with the same arguments
|
184
|
+
as the previous execution, which is useful for retrying a flow.
|
185
|
+
|
186
|
+
Raises:
|
187
|
+
Honk: If there is no current flow run
|
188
|
+
"""
|
89
189
|
flow_run = get_current_flow_run()
|
90
190
|
if flow_run is None:
|
91
191
|
raise Honk("No current flow run")
|
@@ -110,6 +210,32 @@ def flow[FlowArgumentsT: FlowArguments](
|
|
110
210
|
store: IFlowRunStore | None = None,
|
111
211
|
agent_logger: IAgentLogger | None = None,
|
112
212
|
) -> Flow[FlowArgumentsT] | Callable[[IGenerator[FlowArgumentsT]], Flow[FlowArgumentsT]]:
|
213
|
+
"""Decorator for creating Flow instances.
|
214
|
+
|
215
|
+
This decorator transforms async functions into Flow objects that can
|
216
|
+
orchestrate task execution, manage state, and handle persistence.
|
217
|
+
|
218
|
+
Examples:
|
219
|
+
```python
|
220
|
+
@flow
|
221
|
+
async def my_workflow(*, flow_arguments: MyFlowArguments, agent: Agent) -> None:
|
222
|
+
# Flow implementation...
|
223
|
+
|
224
|
+
# Or with parameters
|
225
|
+
@flow(name="custom_name", store=CustomStore())
|
226
|
+
async def my_workflow(*, flow_arguments: MyFlowArguments, agent: Agent) -> None:
|
227
|
+
# Flow implementation...
|
228
|
+
```
|
229
|
+
|
230
|
+
Args:
|
231
|
+
fn: The function to decorate
|
232
|
+
name: Optional custom name for the flow
|
233
|
+
store: Optional store for persisting flow runs
|
234
|
+
agent_logger: Optional logger for agent responses
|
235
|
+
|
236
|
+
Returns:
|
237
|
+
A Flow instance, or a decorator function if used with parameters
|
238
|
+
"""
|
113
239
|
if fn is None:
|
114
240
|
|
115
241
|
def decorator(fn: IGenerator[FlowArgumentsT]) -> Flow[FlowArgumentsT]:
|
goose/_internal/result.py
CHANGED
@@ -1,20 +1,56 @@
|
|
1
|
+
"""Result models for agent responses.
|
2
|
+
|
3
|
+
This module provides the base classes and models for structured results from LLM agents.
|
4
|
+
"""
|
5
|
+
|
1
6
|
from pydantic import BaseModel, ConfigDict, Field
|
2
7
|
|
3
8
|
|
4
9
|
class Result(BaseModel):
|
10
|
+
"""Base class for all result models.
|
11
|
+
|
12
|
+
All result models in Goose extend from this class to have consistent
|
13
|
+
behavior. Results are frozen (immutable) by default.
|
14
|
+
"""
|
15
|
+
|
5
16
|
model_config = ConfigDict(frozen=True)
|
6
17
|
|
7
18
|
|
8
19
|
class TextResult(Result):
|
20
|
+
"""Simple text result model.
|
21
|
+
|
22
|
+
A basic result type that contains unstructured text output from an agent.
|
23
|
+
|
24
|
+
Attributes:
|
25
|
+
text: The text content of the result
|
26
|
+
"""
|
27
|
+
|
9
28
|
text: str
|
10
29
|
|
11
30
|
|
12
31
|
class Replacement(BaseModel):
|
32
|
+
"""Represents a text replacement operation.
|
33
|
+
|
34
|
+
Used in find-and-replace operations to refine structured results.
|
35
|
+
|
36
|
+
Attributes:
|
37
|
+
find: The text to find in the original result
|
38
|
+
replace: The text to replace the found text with
|
39
|
+
"""
|
40
|
+
|
13
41
|
find: str = Field(description="Text to find, to be replaced with `replace`")
|
14
42
|
replace: str = Field(description="Text to replace `find` with")
|
15
43
|
|
16
44
|
|
17
45
|
class FindReplaceResponse(BaseModel):
|
46
|
+
"""Model for find-and-replace operations on structured results.
|
47
|
+
|
48
|
+
Used to refine existing results by applying a series of replacements.
|
49
|
+
|
50
|
+
Attributes:
|
51
|
+
replacements: List of replacement operations to apply
|
52
|
+
"""
|
53
|
+
|
18
54
|
replacements: list[Replacement] = Field(
|
19
55
|
description="List of replacements to make in the previous result to satisfy the user's request"
|
20
56
|
)
|
goose/_internal/task.py
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
"""Task module for defining and executing LLM tasks.
|
2
|
+
|
3
|
+
This module provides the Task class and task decorator for creating and executing
|
4
|
+
individual LLM tasks within a flow. Tasks handle execution, caching, conversation
|
5
|
+
management, and result refinement.
|
6
|
+
"""
|
7
|
+
|
1
8
|
import hashlib
|
2
9
|
from collections.abc import Awaitable, Callable
|
3
10
|
from typing import Any, overload
|
@@ -12,6 +19,16 @@ from goose.errors import Honk
|
|
12
19
|
|
13
20
|
|
14
21
|
class Task[**P, R: Result]:
|
22
|
+
"""A task within a flow that produces a structured result.
|
23
|
+
|
24
|
+
Tasks are the building blocks of flows and represent individual LLM operations.
|
25
|
+
They handle execution, result caching, conversation management, and result refinement.
|
26
|
+
|
27
|
+
Type Parameters:
|
28
|
+
P: Parameter types for the task function
|
29
|
+
R: Result type for the task, must be a subclass of Result
|
30
|
+
"""
|
31
|
+
|
15
32
|
def __init__(
|
16
33
|
self,
|
17
34
|
generator: Callable[P, Awaitable[R]],
|
@@ -19,11 +36,25 @@ class Task[**P, R: Result]:
|
|
19
36
|
*,
|
20
37
|
retries: int = 0,
|
21
38
|
) -> None:
|
39
|
+
"""Initialize a Task.
|
40
|
+
|
41
|
+
Args:
|
42
|
+
generator: The function that implements the task
|
43
|
+
retries: Number of automatic retries if the task fails
|
44
|
+
"""
|
22
45
|
self._generator = generator
|
23
46
|
self._retries = retries
|
24
47
|
|
25
48
|
@property
|
26
49
|
def result_type(self) -> type[R]:
|
50
|
+
"""Get the return type of the task.
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
The result type class for this task
|
54
|
+
|
55
|
+
Raises:
|
56
|
+
Honk: If the task function has no return type annotation
|
57
|
+
"""
|
27
58
|
result_type = self._generator.__annotations__.get("return")
|
28
59
|
if result_type is None:
|
29
60
|
raise Honk(f"Task {self.name} has no return type annotation")
|
@@ -31,9 +62,27 @@ class Task[**P, R: Result]:
|
|
31
62
|
|
32
63
|
@property
|
33
64
|
def name(self) -> str:
|
65
|
+
"""Get the name of the task.
|
66
|
+
|
67
|
+
Returns:
|
68
|
+
The name of the task, derived from the generator function name
|
69
|
+
"""
|
34
70
|
return self._generator.__name__
|
35
71
|
|
36
72
|
async def generate(self, state: NodeState, *args: P.args, **kwargs: P.kwargs) -> R:
|
73
|
+
"""Generate a result for this task.
|
74
|
+
|
75
|
+
Executes the task generator function to produce a result. Uses caching
|
76
|
+
based on input hashing to avoid redundant executions.
|
77
|
+
|
78
|
+
Args:
|
79
|
+
state: The current node state for this task execution
|
80
|
+
*args: Positional arguments for the task function
|
81
|
+
**kwargs: Keyword arguments for the task function
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
The generated result, either freshly computed or from cache
|
85
|
+
"""
|
37
86
|
state_hash = self.__hash_task_call(*args, **kwargs)
|
38
87
|
if state_hash != state.last_hash:
|
39
88
|
result = await self._generator(*args, **kwargs)
|
@@ -51,6 +100,24 @@ class Task[**P, R: Result]:
|
|
51
100
|
context: LLMSystemMessage | None = None,
|
52
101
|
index: int = 0,
|
53
102
|
) -> str:
|
103
|
+
"""Ask a follow-up question about a task's result.
|
104
|
+
|
105
|
+
This method allows for a conversational interaction with a task after
|
106
|
+
it has been executed, to ask questions or get explanations about the result.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
user_message: The user's question or message
|
110
|
+
router: LLM router for routing the request
|
111
|
+
model: The model to use for generating the response
|
112
|
+
context: Optional system message to provide context
|
113
|
+
index: Index of the task instance when the same task appears multiple times
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
The response text from the model
|
117
|
+
|
118
|
+
Raises:
|
119
|
+
Honk: If the task has not been initially generated
|
120
|
+
"""
|
54
121
|
flow_run = self.__get_current_flow_run()
|
55
122
|
node_state = flow_run.get_state(task=self, index=index)
|
56
123
|
|
@@ -82,6 +149,24 @@ class Task[**P, R: Result]:
|
|
82
149
|
context: LLMSystemMessage | None = None,
|
83
150
|
index: int = 0,
|
84
151
|
) -> R:
|
152
|
+
"""Refine a task's result based on feedback.
|
153
|
+
|
154
|
+
This method allows for iterative refinement of a task's result based on
|
155
|
+
user feedback or additional requirements.
|
156
|
+
|
157
|
+
Args:
|
158
|
+
user_message: The user's feedback or refinement request
|
159
|
+
router: LLM router for routing the request
|
160
|
+
model: The model to use for generating the response
|
161
|
+
context: Optional system message to provide context
|
162
|
+
index: Index of the task instance when the same task appears multiple times
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
A refined result of the same type as the original result
|
166
|
+
|
167
|
+
Raises:
|
168
|
+
Honk: If the task has not been initially generated
|
169
|
+
"""
|
85
170
|
flow_run = self.__get_current_flow_run()
|
86
171
|
node_state = flow_run.get_state(task=self, index=index)
|
87
172
|
|
@@ -106,18 +191,46 @@ class Task[**P, R: Result]:
|
|
106
191
|
return result
|
107
192
|
|
108
193
|
def edit(self, *, result: R, index: int = 0) -> None:
|
194
|
+
"""Manually edit a task's result.
|
195
|
+
|
196
|
+
This method allows for direct editing of a task's result, bypassing
|
197
|
+
the usual generation process.
|
198
|
+
|
199
|
+
Args:
|
200
|
+
result: The new result to use
|
201
|
+
index: Index of the task instance to edit
|
202
|
+
"""
|
109
203
|
flow_run = self.__get_current_flow_run()
|
110
204
|
node_state = flow_run.get_state(task=self, index=index)
|
111
205
|
node_state.edit_last_result(result=result.model_dump_json())
|
112
206
|
flow_run.upsert_node_state(node_state)
|
113
207
|
|
114
208
|
def undo(self, *, index: int = 0) -> None:
|
209
|
+
"""Undo the most recent change to a task's state.
|
210
|
+
|
211
|
+
This method reverts the task's state to its previous version.
|
212
|
+
|
213
|
+
Args:
|
214
|
+
index: Index of the task instance to undo
|
215
|
+
"""
|
115
216
|
flow_run = self.__get_current_flow_run()
|
116
217
|
node_state = flow_run.get_state(task=self, index=index)
|
117
218
|
node_state.undo()
|
118
219
|
flow_run.upsert_node_state(node_state)
|
119
220
|
|
120
221
|
async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
222
|
+
"""Execute the task within a flow.
|
223
|
+
|
224
|
+
This method is called when the task is invoked as a function within a flow.
|
225
|
+
It manages state setup, generation, and state persistence.
|
226
|
+
|
227
|
+
Args:
|
228
|
+
*args: Positional arguments for the task function
|
229
|
+
**kwargs: Keyword arguments for the task function
|
230
|
+
|
231
|
+
Returns:
|
232
|
+
The result of the task
|
233
|
+
"""
|
121
234
|
flow_run = self.__get_current_flow_run()
|
122
235
|
node_state = flow_run.get_next_state(task=self)
|
123
236
|
result = await self.generate(node_state, *args, **kwargs)
|
@@ -125,6 +238,22 @@ class Task[**P, R: Result]:
|
|
125
238
|
return result
|
126
239
|
|
127
240
|
def __hash_task_call(self, *args: P.args, **kwargs: P.kwargs) -> int:
|
241
|
+
"""Create a hash of task arguments for caching.
|
242
|
+
|
243
|
+
Generates a unique hash based on the task's input arguments
|
244
|
+
to determine when inputs have changed and results need to be regenerated.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
*args: Positional arguments to hash
|
248
|
+
**kwargs: Keyword arguments to hash
|
249
|
+
|
250
|
+
Returns:
|
251
|
+
An integer hash of the arguments
|
252
|
+
|
253
|
+
Raises:
|
254
|
+
Honk: If an argument cannot be hashed
|
255
|
+
"""
|
256
|
+
|
128
257
|
def update_hash(argument: Any, current_hash: Any = hashlib.sha256()) -> None:
|
129
258
|
try:
|
130
259
|
if isinstance(argument, list | tuple | set):
|
@@ -152,6 +281,14 @@ class Task[**P, R: Result]:
|
|
152
281
|
return int(result.hexdigest(), 16)
|
153
282
|
|
154
283
|
def __get_current_flow_run(self) -> FlowRun[Any]:
|
284
|
+
"""Get the current flow run.
|
285
|
+
|
286
|
+
Returns:
|
287
|
+
The current flow run
|
288
|
+
|
289
|
+
Raises:
|
290
|
+
Honk: If there is no current flow run
|
291
|
+
"""
|
155
292
|
run = get_current_flow_run()
|
156
293
|
if run is None:
|
157
294
|
raise Honk("No current flow run")
|
@@ -168,6 +305,30 @@ def task[**P, R: Result](
|
|
168
305
|
*,
|
169
306
|
retries: int = 0,
|
170
307
|
) -> Task[P, R] | Callable[[Callable[P, Awaitable[R]]], Task[P, R]]:
|
308
|
+
"""Decorator for creating Task instances.
|
309
|
+
|
310
|
+
This decorator transforms async functions into Task objects that can be
|
311
|
+
used in flows. Tasks handle execution, caching, conversation, and result refinement.
|
312
|
+
|
313
|
+
Examples:
|
314
|
+
```python
|
315
|
+
@task
|
316
|
+
async def generate_summary(text: str) -> TextResult:
|
317
|
+
# Task implementation...
|
318
|
+
|
319
|
+
# Or with parameters
|
320
|
+
@task(retries=2)
|
321
|
+
async def generate_summary(text: str) -> TextResult:
|
322
|
+
# Task implementation...
|
323
|
+
```
|
324
|
+
|
325
|
+
Args:
|
326
|
+
generator: The function to decorate
|
327
|
+
retries: Number of automatic retries if the task fails
|
328
|
+
|
329
|
+
Returns:
|
330
|
+
A Task instance, or a decorator function if used with parameters
|
331
|
+
"""
|
171
332
|
if generator is None:
|
172
333
|
|
173
334
|
def decorator(fn: Callable[P, Awaitable[R]]) -> Task[P, R]:
|
goose/errors.py
CHANGED
@@ -1,2 +1,16 @@
|
|
1
|
+
"""Errors module for Goose.
|
2
|
+
|
3
|
+
This module defines the custom exception classes used in the Goose framework.
|
4
|
+
"""
|
5
|
+
|
6
|
+
|
1
7
|
class Honk(Exception):
|
8
|
+
"""Base exception class for Goose framework errors.
|
9
|
+
|
10
|
+
This exception is raised when an error occurs in the Goose framework,
|
11
|
+
such as invalid task configuration, missing requirements, or runtime errors.
|
12
|
+
|
13
|
+
The name "Honk" follows the goose theme of the framework.
|
14
|
+
"""
|
15
|
+
|
2
16
|
pass
|
@@ -0,0 +1,248 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: goose-py
|
3
|
+
Version: 0.12.0
|
4
|
+
Summary: A tool for AI workflows based on human-computer collaboration and structured output.
|
5
|
+
Author-email: Nash Taylor <nash@chelle.ai>, Joshua Cook <joshua@chelle.ai>, Michael Sankur <michael@chelle.ai>
|
6
|
+
Requires-Python: >=3.12
|
7
|
+
Requires-Dist: aikernel==0.1.43
|
8
|
+
Requires-Dist: jsonpath-ng>=1.7.0
|
9
|
+
Requires-Dist: pydantic>=2.8.2
|
10
|
+
Description-Content-Type: text/markdown
|
11
|
+
|
12
|
+
# Goose
|
13
|
+
|
14
|
+
Goose is a framework for building LLM-based agents and workflows with strong typing and state management. Here's what's fundamentally possible:
|
15
|
+
|
16
|
+
1. Structured LLM interactions - Organize model calls with typed inputs/outputs
|
17
|
+
2. Task orchestration - Create reusable tasks that can be composed into flows
|
18
|
+
3. Stateful conversations - Maintain conversation history and model outputs
|
19
|
+
4. Result caching - Avoid redundant computation based on input hashing
|
20
|
+
5. Iterative refinement - Enhance results through progressive feedback loops
|
21
|
+
6. Result validation - Ensure model outputs conform to expected schemas
|
22
|
+
7. Run persistence - Save and reload workflow executions
|
23
|
+
8. Custom logging - Track telemetry and performance metrics
|
24
|
+
|
25
|
+
It enables building reliable, maintainable AI applications with proper error handling, state tracking, and flow control while ensuring type safety throughout.
|
26
|
+
|
27
|
+
## Key Features
|
28
|
+
|
29
|
+
### Structured LLM Interactions
|
30
|
+
|
31
|
+
Organize model calls with typed inputs and outputs using Pydantic models. This ensures that responses from language models conform to expected structures.
|
32
|
+
|
33
|
+
```mermaid
|
34
|
+
graph LR
|
35
|
+
A[User Input] --> B[Agent]
|
36
|
+
B --> C[LLM Model]
|
37
|
+
C --> D[Structured Response]
|
38
|
+
D --> E[Validated Result]
|
39
|
+
E --> F[Application Logic]
|
40
|
+
|
41
|
+
classDef user fill:#f9f,stroke:#333,stroke-width:2px
|
42
|
+
classDef llm fill:#bbf,stroke:#333,stroke-width:2px
|
43
|
+
classDef validation fill:#bfb,stroke:#333,stroke-width:2px
|
44
|
+
|
45
|
+
class A user
|
46
|
+
class C llm
|
47
|
+
class D,E validation
|
48
|
+
```
|
49
|
+
|
50
|
+
### Task Orchestration
|
51
|
+
|
52
|
+
Create reusable tasks that can be composed into flows. Tasks are decorated functions that handle specific operations, while flows coordinate multiple tasks.
|
53
|
+
|
54
|
+
```mermaid
|
55
|
+
graph TD
|
56
|
+
A[Flow] --> B[Task 1]
|
57
|
+
A --> C[Task 2]
|
58
|
+
A --> D[Task 3]
|
59
|
+
B --> E[Result 1]
|
60
|
+
C --> F[Result 2]
|
61
|
+
D --> G[Result 3]
|
62
|
+
E --> H[Flow Output]
|
63
|
+
F --> H
|
64
|
+
G --> H
|
65
|
+
|
66
|
+
classDef flow fill:#f9f,stroke:#333,stroke-width:2px
|
67
|
+
classDef task fill:#bbf,stroke:#333,stroke-width:2px
|
68
|
+
classDef result fill:#bfb,stroke:#333,stroke-width:2px
|
69
|
+
|
70
|
+
class A flow
|
71
|
+
class B,C,D task
|
72
|
+
class E,F,G,H result
|
73
|
+
```
|
74
|
+
|
75
|
+
### Stateful Conversations
|
76
|
+
|
77
|
+
Maintain conversation history and model outputs across multiple interactions. The framework tracks the state of each task in a flow.
|
78
|
+
|
79
|
+
```mermaid
|
80
|
+
sequenceDiagram
|
81
|
+
participant User
|
82
|
+
participant Flow
|
83
|
+
participant Task
|
84
|
+
participant Agent
|
85
|
+
participant LLM
|
86
|
+
|
87
|
+
User->>Flow: Start Conversation
|
88
|
+
Flow->>Task: Execute
|
89
|
+
Task->>Agent: Generate Response
|
90
|
+
Agent->>LLM: Send Messages
|
91
|
+
LLM-->>Agent: Generate Response
|
92
|
+
Agent-->>Task: Store Result
|
93
|
+
Task-->>Flow: Update State
|
94
|
+
Flow-->>User: Return Result
|
95
|
+
|
96
|
+
User->>Flow: Follow-up Question
|
97
|
+
Flow->>Task: Get State
|
98
|
+
Task->>Agent: Send Previous Context + New Question
|
99
|
+
Agent->>LLM: Send Updated Messages
|
100
|
+
LLM-->>Agent: Generate Response
|
101
|
+
Agent-->>Task: Update Conversation
|
102
|
+
Task-->>Flow: Update State
|
103
|
+
Flow-->>User: Return Result
|
104
|
+
```
|
105
|
+
|
106
|
+
### Result Caching
|
107
|
+
|
108
|
+
Avoid redundant computation by caching results based on input hashing. The framework automatically detects when inputs change and only regenerates results when necessary.
|
109
|
+
|
110
|
+
```mermaid
|
111
|
+
flowchart TD
|
112
|
+
A[Task Call] --> B{Inputs Changed?}
|
113
|
+
B -- Yes --> C[Execute Task]
|
114
|
+
B -- No --> D[Return Cached Result]
|
115
|
+
C --> E[Cache Result]
|
116
|
+
E --> F[Return Result]
|
117
|
+
D --> F
|
118
|
+
|
119
|
+
classDef decision fill:#f9f,stroke:#333,stroke-width:2px
|
120
|
+
classDef action fill:#bbf,stroke:#333,stroke-width:2px
|
121
|
+
classDef cache fill:#bfb,stroke:#333,stroke-width:2px
|
122
|
+
|
123
|
+
class B decision
|
124
|
+
class A,C,F action
|
125
|
+
class D,E cache
|
126
|
+
```
|
127
|
+
|
128
|
+
### Iterative Refinement
|
129
|
+
|
130
|
+
Enhance results through progressive feedback loops. The framework supports asking follow-up questions about results and refining them based on feedback.
|
131
|
+
|
132
|
+
```mermaid
|
133
|
+
sequenceDiagram
|
134
|
+
participant User
|
135
|
+
participant Task
|
136
|
+
participant Agent
|
137
|
+
participant LLM
|
138
|
+
|
139
|
+
User->>Task: Generate Initial Result
|
140
|
+
Task->>Agent: Send Request
|
141
|
+
Agent->>LLM: Generate Structured Output
|
142
|
+
LLM-->>Agent: Return Output
|
143
|
+
Agent-->>Task: Store Result
|
144
|
+
Task-->>User: Return Result
|
145
|
+
|
146
|
+
User->>Task: Request Refinement
|
147
|
+
Task->>Agent: Send Feedback + Original Result
|
148
|
+
Agent->>LLM: Generate Find/Replace Operations
|
149
|
+
LLM-->>Agent: Return Changes
|
150
|
+
Agent-->>Task: Apply Changes to Result
|
151
|
+
Task-->>User: Return Refined Result
|
152
|
+
```
|
153
|
+
|
154
|
+
### Result Validation
|
155
|
+
|
156
|
+
Ensure model outputs conform to expected schemas using Pydantic validation. All results must conform to predefined models.
|
157
|
+
|
158
|
+
```mermaid
|
159
|
+
flowchart LR
|
160
|
+
A[LLM Response] --> B[Parse JSON]
|
161
|
+
B --> C{Valid Schema?}
|
162
|
+
C -- Yes --> D[Return Validated Result]
|
163
|
+
C -- No --> E[Raise Error]
|
164
|
+
|
165
|
+
classDef input fill:#bbf,stroke:#333,stroke-width:2px
|
166
|
+
classDef validation fill:#f9f,stroke:#333,stroke-width:2px
|
167
|
+
classDef output fill:#bfb,stroke:#333,stroke-width:2px
|
168
|
+
classDef error fill:#fbb,stroke:#333,stroke-width:2px
|
169
|
+
|
170
|
+
class A input
|
171
|
+
class B,C validation
|
172
|
+
class D output
|
173
|
+
class E error
|
174
|
+
```
|
175
|
+
|
176
|
+
### Run Persistence
|
177
|
+
|
178
|
+
Save and reload workflow executions. The framework provides interfaces for storing flow runs, allowing for resuming work or reviewing past executions.
|
179
|
+
|
180
|
+
```mermaid
|
181
|
+
graph TD
|
182
|
+
A[Start Flow] --> B[Create Flow Run]
|
183
|
+
B --> C[Execute Tasks]
|
184
|
+
C --> D[Save Run State]
|
185
|
+
D --> E[End Flow]
|
186
|
+
|
187
|
+
F[Later Time] --> G[Load Saved Run]
|
188
|
+
G --> H[Resume Execution]
|
189
|
+
H --> D
|
190
|
+
|
191
|
+
classDef flow fill:#f9f,stroke:#333,stroke-width:2px
|
192
|
+
classDef execution fill:#bbf,stroke:#333,stroke-width:2px
|
193
|
+
classDef storage fill:#bfb,stroke:#333,stroke-width:2px
|
194
|
+
|
195
|
+
class A,E,F flow
|
196
|
+
class B,C,H execution
|
197
|
+
class D,G storage
|
198
|
+
```
|
199
|
+
|
200
|
+
### Custom Logging
|
201
|
+
|
202
|
+
Track telemetry and performance metrics. The framework supports custom loggers to record model usage, token counts, and execution time.
|
203
|
+
|
204
|
+
```mermaid
|
205
|
+
flowchart TD
|
206
|
+
A[Agent Call] --> B[Execute LLM Request]
|
207
|
+
B --> C[Record Metrics]
|
208
|
+
C --> D{Custom Logger?}
|
209
|
+
D -- Yes --> E[Send to Custom Logger]
|
210
|
+
D -- No --> F[Log to Default Logger]
|
211
|
+
E --> G[Return Result]
|
212
|
+
F --> G
|
213
|
+
|
214
|
+
classDef action fill:#bbf,stroke:#333,stroke-width:2px
|
215
|
+
classDef logging fill:#bfb,stroke:#333,stroke-width:2px
|
216
|
+
classDef decision fill:#f9f,stroke:#333,stroke-width:2px
|
217
|
+
|
218
|
+
class A,B,G action
|
219
|
+
class C,E,F logging
|
220
|
+
class D decision
|
221
|
+
```
|
222
|
+
|
223
|
+
## Building with Goose
|
224
|
+
|
225
|
+
Goose enables building reliable, maintainable AI applications with proper error handling, state tracking, and flow control while ensuring type safety throughout. This approach reduces common issues in LLM applications like:
|
226
|
+
|
227
|
+
- Type inconsistencies in model responses
|
228
|
+
- Loss of context between interactions
|
229
|
+
- Redundant LLM calls for identical inputs
|
230
|
+
- Difficulty in resuming interrupted workflows
|
231
|
+
- Lack of structured error handling
|
232
|
+
|
233
|
+
Start building more robust LLM applications with Goose's typed, stateful approach to agent development.
|
234
|
+
|
235
|
+
## Installation and Package Management
|
236
|
+
|
237
|
+
Goose uses `uv` for package management. Never use pip with this project.
|
238
|
+
|
239
|
+
```bash
|
240
|
+
# Install dependencies
|
241
|
+
uv add <package-name>
|
242
|
+
|
243
|
+
# Update dependencies file
|
244
|
+
uv sync
|
245
|
+
|
246
|
+
# Run commands
|
247
|
+
uv run <command>
|
248
|
+
```
|
@@ -1,18 +1,18 @@
|
|
1
|
-
goose/__init__.py,sha256=
|
2
|
-
goose/errors.py,sha256
|
1
|
+
goose/__init__.py,sha256=58IFJL7Zd4WMqlbWhOtDVbU4sUegFjTIYuGsDQJAZU4,842
|
2
|
+
goose/errors.py,sha256=FtCv1uGzsDijktcFePVvAQd0rC0MuSBLFIh8YwwJ_BU,429
|
3
3
|
goose/flow.py,sha256=YsZLBa5I1W27_P6LYGWbtFX8ZYx9vJG3KtENYChHm5E,111
|
4
4
|
goose/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
goose/runs.py,sha256=ub-r_gzbUbaIzWXX-jc-dncNxEh6zTfzIkmnDfCSbRI,160
|
6
6
|
goose/task.py,sha256=95rspdxETJoY12IHBl3KjnVIdqQnf1jDKlnGWNWOTvQ,53
|
7
|
-
goose/_internal/agent.py,sha256=
|
7
|
+
goose/_internal/agent.py,sha256=K1dKXsfI1a7W8RUNoDcEOs4zX_1-LCHvOEcxzsTiicg,13405
|
8
8
|
goose/_internal/conversation.py,sha256=vhJwe1pHk2lV60DaB9Tz9KbpzQo7_thRYInPjbIoUTE,1437
|
9
|
-
goose/_internal/flow.py,sha256=
|
10
|
-
goose/_internal/result.py,sha256=
|
9
|
+
goose/_internal/flow.py,sha256=nfmy-RyWnt1jh0TBMpy-YpX8ayltjUcPtLOZ4mDFfds,7947
|
10
|
+
goose/_internal/result.py,sha256=MXH1jhZSzN_T1ZkslJk6b49otovBlWLhiyr3pAmT1OM,1522
|
11
11
|
goose/_internal/state.py,sha256=kA116MpsetsQz6nYodsXOqE3uYz37OTgjC9Vcy_3Qvg,8065
|
12
12
|
goose/_internal/store.py,sha256=tWmKfa1-yq1jU6lT3l6kSOmVt2m3H7I1xLMTrxnUDI8,889
|
13
|
-
goose/_internal/task.py,sha256=
|
13
|
+
goose/_internal/task.py,sha256=I2COwqtRFkO46jvberh_XMMnPCOlJu4Y5-JajMUjaG4,11672
|
14
14
|
goose/_internal/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
goose/_internal/types/telemetry.py,sha256=xpfkhx7zCdZjjKU8rPuUJ2UHgqmZ084ZupzbTU36gSM,3791
|
16
|
-
goose_py-0.
|
17
|
-
goose_py-0.
|
18
|
-
goose_py-0.
|
16
|
+
goose_py-0.12.0.dist-info/METADATA,sha256=qCPOuLUaoejhbU6C-EhVt0aqPPpE3xpi8VSWf9tyrbM,7621
|
17
|
+
goose_py-0.12.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
18
|
+
goose_py-0.12.0.dist-info/RECORD,,
|
@@ -1,14 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: goose-py
|
3
|
-
Version: 0.11.25
|
4
|
-
Summary: A tool for AI workflows based on human-computer collaboration and structured output.
|
5
|
-
Author-email: Nash Taylor <nash@chelle.ai>, Joshua Cook <joshua@chelle.ai>, Michael Sankur <michael@chelle.ai>
|
6
|
-
Requires-Python: >=3.12
|
7
|
-
Requires-Dist: aikernel==0.1.42
|
8
|
-
Requires-Dist: jsonpath-ng>=1.7.0
|
9
|
-
Requires-Dist: pydantic>=2.8.2
|
10
|
-
Description-Content-Type: text/markdown
|
11
|
-
|
12
|
-
# Goose
|
13
|
-
|
14
|
-
Docs to come.
|
File without changes
|