goose-py 0.8.0__py3-none-any.whl → 0.9.1__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 CHANGED
@@ -1,4 +1,4 @@
1
1
  from goose._internal.agent import Agent
2
- from goose._internal.flow import flow
2
+ from goose._internal.flow import flow, FlowArguments
3
3
  from goose._internal.result import Result, TextResult
4
4
  from goose._internal.task import task
goose/_internal/flow.py CHANGED
@@ -1,4 +1,4 @@
1
- from collections.abc import AsyncIterator, Awaitable, Callable
1
+ from collections.abc import AsyncIterator, Callable
2
2
  from contextlib import asynccontextmanager
3
3
  from types import CodeType
4
4
  from typing import Protocol, overload
@@ -6,48 +6,62 @@ from typing import Protocol, overload
6
6
  from goose._internal.agent import Agent, IAgentLogger
7
7
  from goose._internal.conversation import Conversation
8
8
  from goose._internal.result import Result
9
- from goose._internal.state import FlowRun, get_current_flow_run, set_current_flow_run
9
+ from goose._internal.state import FlowArguments, FlowRun, get_current_flow_run, set_current_flow_run
10
10
  from goose._internal.store import IFlowRunStore, InMemoryFlowRunStore
11
11
  from goose.errors import Honk
12
12
 
13
13
 
14
+ class IGenerator[FlowArgumentsT: FlowArguments](Protocol):
15
+ __name__: str
16
+
17
+ async def __call__(self, *, flow_arguments: FlowArgumentsT, agent: Agent) -> None: ...
18
+
19
+
14
20
  class IAdapter[ResultT: Result](Protocol):
15
21
  __code__: CodeType
16
22
 
17
23
  async def __call__(self, *, conversation: Conversation[ResultT], agent: Agent) -> ResultT: ...
18
24
 
19
25
 
20
- class Flow[**P]:
26
+ class Flow[FlowArgumentsT: FlowArguments]:
21
27
  def __init__(
22
28
  self,
23
- fn: Callable[P, Awaitable[None]],
29
+ fn: IGenerator[FlowArgumentsT],
24
30
  /,
25
31
  *,
26
32
  name: str | None = None,
27
- store: IFlowRunStore | None = None,
33
+ store: IFlowRunStore[FlowArgumentsT] | None = None,
28
34
  agent_logger: IAgentLogger | None = None,
29
35
  ) -> None:
30
36
  self._fn = fn
31
37
  self._name = name
32
38
  self._agent_logger = agent_logger
33
- self._store = store or InMemoryFlowRunStore(flow_name=self.name)
39
+ self._store = store or InMemoryFlowRunStore(flow_name=self.name, flow_arguments_model=self.flow_arguments_model)
40
+
41
+ @property
42
+ def flow_arguments_model(self) -> type[FlowArgumentsT]:
43
+ arguments_model = self._fn.__annotations__.get("flow_arguments")
44
+ if arguments_model is None:
45
+ raise Honk("Flow function has an invalid signature. Must accept `flow_arguments` and `agent` as arguments.")
46
+
47
+ return arguments_model
34
48
 
35
49
  @property
36
50
  def name(self) -> str:
37
51
  return self._name or self._fn.__name__
38
52
 
39
53
  @property
40
- def current_run(self) -> FlowRun:
54
+ def current_run(self) -> FlowRun[FlowArgumentsT]:
41
55
  run = get_current_flow_run()
42
56
  if run is None:
43
57
  raise Honk("No current flow run")
44
58
  return run
45
59
 
46
60
  @asynccontextmanager
47
- async def start_run(self, *, run_id: str) -> AsyncIterator[FlowRun]:
61
+ async def start_run(self, *, run_id: str) -> AsyncIterator[FlowRun[FlowArgumentsT]]:
48
62
  existing_run = await self._store.get(run_id=run_id)
49
63
  if existing_run is None:
50
- run = FlowRun()
64
+ run = FlowRun(flow_arguments_model=self.flow_arguments_model)
51
65
  else:
52
66
  run = existing_run
53
67
 
@@ -61,43 +75,42 @@ class Flow[**P]:
61
75
 
62
76
  set_current_flow_run(old_run)
63
77
 
64
- async def generate(self, *args: P.args, **kwargs: P.kwargs) -> None:
78
+ async def generate(self, flow_arguments: FlowArgumentsT, /) -> None:
65
79
  flow_run = get_current_flow_run()
66
80
  if flow_run is None:
67
81
  raise Honk("No current flow run")
68
82
 
69
- flow_run.set_flow_inputs(*args, **kwargs)
70
- await self._fn(*args, **kwargs)
83
+ flow_run.set_flow_arguments(flow_arguments)
84
+ await self._fn(flow_arguments=flow_arguments, agent=flow_run.agent)
71
85
 
72
86
  async def regenerate(self) -> None:
73
87
  flow_run = get_current_flow_run()
74
88
  if flow_run is None:
75
89
  raise Honk("No current flow run")
76
90
 
77
- flow_args, flow_kwargs = flow_run.flow_inputs
78
- await self._fn(*flow_args, **flow_kwargs)
91
+ await self._fn(flow_arguments=flow_run.flow_arguments, agent=flow_run.agent)
79
92
 
80
93
 
81
94
  @overload
82
- def flow[**P](fn: Callable[P, Awaitable[None]], /) -> Flow[P]: ...
95
+ def flow[FlowArgumentsT: FlowArguments](fn: IGenerator[FlowArgumentsT], /) -> Flow[FlowArgumentsT]: ...
83
96
  @overload
84
- def flow[**P](
97
+ def flow[FlowArgumentsT: FlowArguments](
85
98
  *,
86
99
  name: str | None = None,
87
- store: IFlowRunStore | None = None,
100
+ store: IFlowRunStore[FlowArgumentsT] | None = None,
88
101
  agent_logger: IAgentLogger | None = None,
89
- ) -> Callable[[Callable[P, Awaitable[None]]], Flow[P]]: ...
90
- def flow[**P](
91
- fn: Callable[P, Awaitable[None]] | None = None,
102
+ ) -> Callable[[IGenerator[FlowArgumentsT]], Flow[FlowArgumentsT]]: ...
103
+ def flow[FlowArgumentsT: FlowArguments](
104
+ fn: IGenerator[FlowArgumentsT] | None = None,
92
105
  /,
93
106
  *,
94
107
  name: str | None = None,
95
- store: IFlowRunStore | None = None,
108
+ store: IFlowRunStore[FlowArgumentsT] | None = None,
96
109
  agent_logger: IAgentLogger | None = None,
97
- ) -> Flow[P] | Callable[[Callable[P, Awaitable[None]]], Flow[P]]:
110
+ ) -> Flow[FlowArgumentsT] | Callable[[IGenerator[FlowArgumentsT]], Flow[FlowArgumentsT]]:
98
111
  if fn is None:
99
112
 
100
- def decorator(fn: Callable[P, Awaitable[None]]) -> Flow[P]:
113
+ def decorator(fn: IGenerator[FlowArgumentsT]) -> Flow[FlowArgumentsT]:
101
114
  return Flow(fn, name=name, store=store, agent_logger=agent_logger)
102
115
 
103
116
  return decorator
goose/_internal/state.py CHANGED
@@ -1,8 +1,8 @@
1
+ import json
1
2
  from contextvars import ContextVar
2
- from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Self
3
+ from typing import TYPE_CHECKING, Any, NewType, Self
4
4
 
5
- from pydantic import BaseModel
5
+ from pydantic import BaseModel, ConfigDict
6
6
 
7
7
  from goose._internal.agent import (
8
8
  Agent,
@@ -17,12 +17,11 @@ from goose.errors import Honk
17
17
  if TYPE_CHECKING:
18
18
  from goose._internal.task import Task
19
19
 
20
+ SerializedFlowRun = NewType("SerializedFlowRun", str)
20
21
 
21
- @dataclass
22
- class FlowRunState:
23
- node_states: dict[tuple[str, int], str]
24
- flow_args: tuple[Any, ...]
25
- flow_kwargs: dict[str, Any]
22
+
23
+ class FlowArguments(BaseModel):
24
+ model_config = ConfigDict(frozen=True)
26
25
 
27
26
 
28
27
  class NodeState[ResultT: Result](BaseModel):
@@ -73,15 +72,15 @@ class NodeState[ResultT: Result](BaseModel):
73
72
  return self
74
73
 
75
74
 
76
- class FlowRun:
77
- def __init__(self) -> None:
75
+ class FlowRun[FlowArgumentsT: FlowArguments]:
76
+ def __init__(self, *, flow_arguments_model: type[FlowArgumentsT]) -> None:
78
77
  self._node_states: dict[tuple[str, int], str] = {}
79
78
  self._last_requested_indices: dict[str, int] = {}
80
79
  self._flow_name = ""
81
80
  self._id = ""
82
81
  self._agent: Agent | None = None
83
- self._flow_args: tuple[Any, ...] | None = None
84
- self._flow_kwargs: dict[str, Any] | None = None
82
+ self._flow_arguments: FlowArgumentsT | None = None
83
+ self._flow_arguments_model = flow_arguments_model
85
84
 
86
85
  @property
87
86
  def flow_name(self) -> str:
@@ -98,11 +97,11 @@ class FlowRun:
98
97
  return self._agent
99
98
 
100
99
  @property
101
- def flow_inputs(self) -> tuple[tuple[Any, ...], dict[str, Any]]:
102
- if self._flow_args is None or self._flow_kwargs is None:
100
+ def flow_arguments(self) -> FlowArgumentsT:
101
+ if self._flow_arguments is None:
103
102
  raise Honk("This Flow run has not been executed before")
104
103
 
105
- return self._flow_args, self._flow_kwargs
104
+ return self._flow_arguments
106
105
 
107
106
  def get_all[R: Result](self, *, task: "Task[Any, R]") -> list[NodeState[R]]:
108
107
  matching_nodes: list[NodeState[R]] = []
@@ -122,9 +121,8 @@ class FlowRun:
122
121
  last_hash=0,
123
122
  )
124
123
 
125
- def set_flow_inputs(self, *args: Any, **kwargs: Any) -> None:
126
- self._flow_args = args
127
- self._flow_kwargs = kwargs
124
+ def set_flow_arguments(self, flow_arguments: FlowArgumentsT, /) -> None:
125
+ self._flow_arguments = flow_arguments
128
126
 
129
127
  def upsert_node_state(self, node_state: NodeState[Any], /) -> None:
130
128
  key = (node_state.task_name, node_state.index)
@@ -161,31 +159,35 @@ class FlowRun:
161
159
  if key in self._node_states:
162
160
  del self._node_states[key]
163
161
 
164
- def dump(self) -> FlowRunState:
165
- flow_args, flow_kwargs = self.flow_inputs
166
-
167
- return FlowRunState(
168
- node_states=self._node_states,
169
- flow_args=flow_args,
170
- flow_kwargs=flow_kwargs,
162
+ def dump(self) -> SerializedFlowRun:
163
+ formatted_node_states = {f"{k[0]},{k[1]}": v for k, v in self._node_states.items()}
164
+ return SerializedFlowRun(
165
+ json.dumps({"node_states": formatted_node_states, "flow_arguments": self.flow_arguments.model_dump()})
171
166
  )
172
167
 
173
168
  @classmethod
174
- def load(cls, flow_run_state: FlowRunState, /) -> Self:
175
- flow_run = cls()
176
- flow_run._node_states = flow_run_state.node_states
177
- flow_run._flow_args = flow_run_state.flow_args
178
- flow_run._flow_kwargs = flow_run_state.flow_kwargs
169
+ def load(cls, *, serialized_flow_run: SerializedFlowRun, flow_arguments_model: type[FlowArgumentsT]) -> Self:
170
+ flow_run_state = json.loads(serialized_flow_run)
171
+ raw_node_states = flow_run_state["node_states"]
172
+ node_states: dict[tuple[str, int], str] = {}
173
+ for key, value in raw_node_states.items():
174
+ task_name, index = key.split(",")
175
+ node_states[(task_name, int(index))] = value
176
+ flow_arguments = flow_arguments_model.model_validate(flow_run_state["flow_arguments"])
177
+
178
+ flow_run = cls(flow_arguments_model=flow_arguments_model)
179
+ flow_run._node_states = node_states
180
+ flow_run._flow_arguments = flow_arguments
179
181
 
180
182
  return flow_run
181
183
 
182
184
 
183
- _current_flow_run: ContextVar[FlowRun | None] = ContextVar("current_flow_run", default=None)
185
+ _current_flow_run: ContextVar[FlowRun[Any] | None] = ContextVar("current_flow_run", default=None)
184
186
 
185
187
 
186
- def get_current_flow_run() -> FlowRun | None:
188
+ def get_current_flow_run() -> FlowRun[Any] | None:
187
189
  return _current_flow_run.get()
188
190
 
189
191
 
190
- def set_current_flow_run(flow_run: FlowRun | None) -> None:
192
+ def set_current_flow_run(flow_run: FlowRun[Any] | None) -> None:
191
193
  _current_flow_run.set(flow_run)
goose/_internal/store.py CHANGED
@@ -3,27 +3,30 @@ from __future__ import annotations
3
3
  from typing import Protocol
4
4
 
5
5
  from goose._internal.flow import FlowRun
6
- from goose._internal.state import FlowRunState
6
+ from goose._internal.state import FlowArguments, SerializedFlowRun
7
7
 
8
8
 
9
- class IFlowRunStore(Protocol):
9
+ class IFlowRunStore[FlowArgumentsT: FlowArguments](Protocol):
10
10
  def __init__(self, *, flow_name: str) -> None: ...
11
- async def get(self, *, run_id: str) -> FlowRun | None: ...
12
- async def save(self, *, run: FlowRun) -> None: ...
11
+ async def get(self, *, run_id: str) -> FlowRun[FlowArgumentsT] | None: ...
12
+ async def save(self, *, run: FlowRun[FlowArgumentsT]) -> None: ...
13
13
  async def delete(self, *, run_id: str) -> None: ...
14
14
 
15
15
 
16
- class InMemoryFlowRunStore(IFlowRunStore):
17
- def __init__(self, *, flow_name: str) -> None:
16
+ class InMemoryFlowRunStore[FlowArgumentsT: FlowArguments](IFlowRunStore[FlowArgumentsT]):
17
+ def __init__(self, *, flow_name: str, flow_arguments_model: type[FlowArgumentsT]) -> None:
18
18
  self._flow_name = flow_name
19
- self._runs: dict[str, FlowRunState] = {}
19
+ self._flow_arguments_model = flow_arguments_model
20
+ self._runs: dict[str, SerializedFlowRun] = {}
20
21
 
21
- async def get(self, *, run_id: str) -> FlowRun | None:
22
- state = self._runs.get(run_id)
23
- if state is not None:
24
- return FlowRun.load(state)
22
+ async def get(self, *, run_id: str) -> FlowRun[FlowArgumentsT] | None:
23
+ serialized_flow_run = self._runs.get(run_id)
24
+ if serialized_flow_run is not None:
25
+ return FlowRun.load(
26
+ serialized_flow_run=serialized_flow_run, flow_arguments_model=self._flow_arguments_model
27
+ )
25
28
 
26
- async def save(self, *, run: FlowRun) -> None:
29
+ async def save(self, *, run: FlowRun[FlowArgumentsT]) -> None:
27
30
  self._runs[run.id] = run.dump()
28
31
 
29
32
  async def delete(self, *, run_id: str) -> None:
goose/_internal/task.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Awaitable, Callable
2
- from typing import overload
2
+ from typing import Any, overload
3
3
 
4
4
  from goose._internal.agent import Agent, GeminiModel, SystemMessage, UserMessage
5
5
  from goose._internal.conversation import Conversation
@@ -111,7 +111,7 @@ class Task[**P, R: Result]:
111
111
  except TypeError:
112
112
  raise Honk(f"Unhashable argument to task {self.name}: {args} {kwargs}")
113
113
 
114
- def __get_current_flow_run(self) -> FlowRun:
114
+ def __get_current_flow_run(self) -> FlowRun[Any]:
115
115
  run = get_current_flow_run()
116
116
  if run is None:
117
117
  raise Honk("No current flow run")
goose/flow.py CHANGED
@@ -1,3 +1,3 @@
1
- from goose._internal.flow import Flow
1
+ from goose._internal.flow import Flow, FlowArguments, IGenerator
2
2
 
3
- __all__ = ["Flow"]
3
+ __all__ = ["Flow", "FlowArguments", "IGenerator"]
goose/runs.py CHANGED
@@ -1,4 +1,4 @@
1
- from goose._internal.state import FlowRun, FlowRunState
1
+ from goose._internal.state import FlowRun, SerializedFlowRun
2
2
  from goose._internal.store import IFlowRunStore
3
3
 
4
- __all__ = ["FlowRun", "FlowRunState", "IFlowRunStore"]
4
+ __all__ = ["FlowRun", "IFlowRunStore", "SerializedFlowRun"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: goose-py
3
- Version: 0.8.0
3
+ Version: 0.9.1
4
4
  Summary: A tool for AI workflows based on human-computer collaboration and structured output.
5
5
  Author-email: Nash Taylor <nash@chelle.ai>, Joshua Cook <joshua@chelle.ai>, Michael Sankur <michael@chelle.ai>
6
6
  Requires-Python: >=3.12
@@ -0,0 +1,18 @@
1
+ goose/__init__.py,sha256=_qg6tofbArI5KO7fWqaFNY5IcbVELTd7oS6uYijPmzg,185
2
+ goose/agent.py,sha256=g2tPFqEhqBABEjmpNJ2ShfjHDGzmeUXIgOZCKDZ2-40,600
3
+ goose/errors.py,sha256=-0OyZQJWYTRw5YgnCB2_uorVaUsL6Z0QYQO2FqzCiyg,32
4
+ goose/flow.py,sha256=mRyGvgyGgy39P5ZDc1L-8_XqPf8HCcrqGrTBDSswn4M,116
5
+ goose/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ goose/runs.py,sha256=kAHw08ayGbwzSH_p-CfsQDvTcA4H09nUuq6zt3gIFxo,170
7
+ goose/_internal/agent.py,sha256=l0pKfShovrs238sKAr-zubtcacYm82TGwQHcBWVJm2g,5875
8
+ goose/_internal/conversation.py,sha256=1OZQ_N6QZE7L_ZpXG2bjoWkVQ-G7h0JvKkqswmQWG58,1202
9
+ goose/_internal/flow.py,sha256=LX6fdDBBgGNPfpSIUFenJ8-EI6tnNRuGZfUpvSb1isg,4128
10
+ goose/_internal/result.py,sha256=-eZJn-2sPo7rHZ38Sz6IAHXqiJ-Ss39esEoFGimJEBI,155
11
+ goose/_internal/state.py,sha256=wJdXHGw_KPqc1Tuv8cHj1loqLct_KHWBNXzbjBv6GAo,6552
12
+ goose/_internal/store.py,sha256=ZdQwYRe1PKkTUd58p0ja1KyyGhEYGVP4gWvYsqOEWuk,1356
13
+ goose/_internal/task.py,sha256=vfmWfxhr76nSF7STMZISFqP9ugt3BkHzraMbEZKuozI,5196
14
+ goose/_internal/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ goose/_internal/types/agent.py,sha256=rNVt2gEr_m4_8tGFgcdichpPp8xhOS5GY0kN2C4tiE8,2153
16
+ goose_py-0.9.1.dist-info/METADATA,sha256=O2kYoXBBpkOUDX1ejc8dBUHv6GI86trC-QTb11HoSAs,441
17
+ goose_py-0.9.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ goose_py-0.9.1.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- goose/__init__.py,sha256=mppYCowcZw9ke_4y1d1ayHwI3502LBaY959jdOVBPp0,170
2
- goose/agent.py,sha256=g2tPFqEhqBABEjmpNJ2ShfjHDGzmeUXIgOZCKDZ2-40,600
3
- goose/errors.py,sha256=-0OyZQJWYTRw5YgnCB2_uorVaUsL6Z0QYQO2FqzCiyg,32
4
- goose/flow.py,sha256=A1bzNIjnoVXRFm6LGhQglxVnKMP0vEVfvTubTol7Kww,58
5
- goose/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- goose/runs.py,sha256=HHcky_IbmY9HWBjpXJgOcH2Ko0N39qADsGIPR8QYpek,160
7
- goose/_internal/agent.py,sha256=l0pKfShovrs238sKAr-zubtcacYm82TGwQHcBWVJm2g,5875
8
- goose/_internal/conversation.py,sha256=1OZQ_N6QZE7L_ZpXG2bjoWkVQ-G7h0JvKkqswmQWG58,1202
9
- goose/_internal/flow.py,sha256=KGT6NpkMY8q_N1yKwWrxfTbhwcu5AwdHtgCPqCdL3F8,3266
10
- goose/_internal/result.py,sha256=-eZJn-2sPo7rHZ38Sz6IAHXqiJ-Ss39esEoFGimJEBI,155
11
- goose/_internal/state.py,sha256=mXwSkTQq5Y6xLSRgvfUPILmK5nbiA-sp8Nunjcra5n4,6045
12
- goose/_internal/store.py,sha256=vIxPIpechF_lEQlQ8JT1NDySDfHe3-eMHEWeTqVbscg,946
13
- goose/_internal/task.py,sha256=gKOheVs4xCvqDLazzgp5Ok6bLz-MuywViiEXWNOQ_gQ,5186
14
- goose/_internal/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- goose/_internal/types/agent.py,sha256=rNVt2gEr_m4_8tGFgcdichpPp8xhOS5GY0kN2C4tiE8,2153
16
- goose_py-0.8.0.dist-info/METADATA,sha256=bg599aFGIn1jHNInnQ36ie5bF-2sgQMEhvV2naYZqi8,441
17
- goose_py-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
- goose_py-0.8.0.dist-info/RECORD,,