vibecore 0.5.0__py3-none-any.whl → 0.6.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.
Potentially problematic release.
This version of vibecore might be problematic. Click here for more details.
- vibecore/agents/default.py +3 -3
- vibecore/agents/task.py +3 -3
- vibecore/cli.py +68 -43
- vibecore/context.py +41 -15
- vibecore/flow.py +144 -122
- vibecore/handlers/stream_handler.py +0 -10
- vibecore/main.py +46 -272
- vibecore/session/loader.py +2 -2
- vibecore/tools/file/executor.py +13 -5
- vibecore/tools/file/tools.py +5 -5
- vibecore/tools/python/helpers.py +2 -2
- vibecore/tools/python/tools.py +2 -2
- vibecore/tools/shell/executor.py +5 -5
- vibecore/tools/shell/tools.py +5 -5
- vibecore/tools/task/executor.py +2 -2
- vibecore/tools/task/tools.py +2 -2
- vibecore/tools/todo/tools.py +3 -3
- vibecore/tools/webfetch/tools.py +1 -4
- vibecore/tools/websearch/tools.py +1 -4
- vibecore/widgets/core.py +2 -9
- {vibecore-0.5.0.dist-info → vibecore-0.6.0.dist-info}/METADATA +91 -31
- {vibecore-0.5.0.dist-info → vibecore-0.6.0.dist-info}/RECORD +25 -25
- {vibecore-0.5.0.dist-info → vibecore-0.6.0.dist-info}/WHEEL +0 -0
- {vibecore-0.5.0.dist-info → vibecore-0.6.0.dist-info}/entry_points.txt +0 -0
- {vibecore-0.5.0.dist-info → vibecore-0.6.0.dist-info}/licenses/LICENSE +0 -0
vibecore/flow.py
CHANGED
|
@@ -4,7 +4,7 @@ import datetime
|
|
|
4
4
|
import sys
|
|
5
5
|
import threading
|
|
6
6
|
from collections.abc import Callable, Coroutine
|
|
7
|
-
from typing import Any,
|
|
7
|
+
from typing import Any, Concatenate, Generic, TypeAlias, cast, overload
|
|
8
8
|
|
|
9
9
|
from agents import (
|
|
10
10
|
Agent,
|
|
@@ -12,7 +12,6 @@ from agents import (
|
|
|
12
12
|
RunHooks,
|
|
13
13
|
Runner,
|
|
14
14
|
Session,
|
|
15
|
-
TContext,
|
|
16
15
|
TResponseInputItem,
|
|
17
16
|
)
|
|
18
17
|
from agents.result import RunResultBase
|
|
@@ -20,7 +19,7 @@ from agents.run import DEFAULT_MAX_TURNS
|
|
|
20
19
|
from textual.pilot import Pilot
|
|
21
20
|
from typing_extensions import TypeVar
|
|
22
21
|
|
|
23
|
-
from vibecore.context import
|
|
22
|
+
from vibecore.context import AppAwareContext
|
|
24
23
|
from vibecore.main import AppIsExiting, VibecoreApp
|
|
25
24
|
from vibecore.session import JSONLSession
|
|
26
25
|
from vibecore.settings import settings
|
|
@@ -28,35 +27,39 @@ from vibecore.widgets.core import MyTextArea
|
|
|
28
27
|
from vibecore.widgets.messages import SystemMessage
|
|
29
28
|
|
|
30
29
|
|
|
31
|
-
class
|
|
32
|
-
"""
|
|
30
|
+
class NoUserInputLeft(Exception):
|
|
31
|
+
"""Raised when no more user inputs are available in static runner."""
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
"""Get user input with optional prompt message.
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
prompt: Optional prompt to display before getting input.
|
|
39
|
-
|
|
40
|
-
Returns:
|
|
41
|
-
The user's input string.
|
|
42
|
-
"""
|
|
43
|
-
...
|
|
33
|
+
pass
|
|
44
34
|
|
|
45
35
|
|
|
36
|
+
TContext = TypeVar("TContext", default=None)
|
|
46
37
|
TWorkflowReturn = TypeVar("TWorkflowReturn", default=RunResultBase)
|
|
47
|
-
DecoratedCallable: TypeAlias = Callable[..., Coroutine[Any, Any, TWorkflowReturn]]
|
|
48
38
|
|
|
49
39
|
|
|
50
|
-
class
|
|
51
|
-
def __init__(
|
|
40
|
+
class VibecoreRunner(Generic[TContext, TWorkflowReturn]):
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
vibecore: "Vibecore[TContext, TWorkflowReturn]",
|
|
44
|
+
context: TContext | None = None,
|
|
45
|
+
session: Session | None = None,
|
|
46
|
+
) -> None:
|
|
52
47
|
self.vibecore = vibecore
|
|
48
|
+
self.context = context
|
|
49
|
+
|
|
50
|
+
if session is None:
|
|
51
|
+
session_id = f"chat-{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
52
|
+
self._session = JSONLSession(
|
|
53
|
+
session_id=session_id,
|
|
54
|
+
project_path=None, # Will use current working directory
|
|
55
|
+
base_dir=settings.session.base_dir,
|
|
56
|
+
)
|
|
57
|
+
else:
|
|
58
|
+
self._session = session
|
|
53
59
|
|
|
54
60
|
@property
|
|
55
61
|
def session(self) -> Session:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
async def user_input(self, prompt: str = "") -> str:
|
|
59
|
-
raise NotImplementedError("user_input method not implemented.")
|
|
62
|
+
return self._session
|
|
60
63
|
|
|
61
64
|
async def print(self, message: str) -> None:
|
|
62
65
|
print(message, file=sys.stderr)
|
|
@@ -86,46 +89,41 @@ class VibecoreRunnerBase(Generic[TWorkflowReturn]):
|
|
|
86
89
|
return result
|
|
87
90
|
|
|
88
91
|
|
|
89
|
-
class
|
|
90
|
-
def __init__(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
base_dir=settings.session.base_dir,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
@property
|
|
101
|
-
def session(self) -> Session:
|
|
102
|
-
return self._session
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
class VibecoreCliRunner(VibecoreSimpleRunner[TWorkflowReturn]):
|
|
106
|
-
def __init__(self, vibecore: "Vibecore[TWorkflowReturn]") -> None:
|
|
107
|
-
super().__init__(vibecore)
|
|
92
|
+
class VibecoreCliRunner(VibecoreRunner[TContext, TWorkflowReturn]):
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
vibecore: "Vibecore[TContext, TWorkflowReturn]",
|
|
96
|
+
context: TContext | None = None,
|
|
97
|
+
session: Session | None = None,
|
|
98
|
+
) -> None:
|
|
99
|
+
super().__init__(vibecore, context=context, session=session)
|
|
108
100
|
|
|
109
|
-
async def
|
|
101
|
+
async def _user_input(self, prompt: str = "") -> str:
|
|
110
102
|
return input(prompt)
|
|
111
103
|
|
|
112
104
|
async def run(self) -> TWorkflowReturn:
|
|
113
105
|
assert self.vibecore.workflow_logic is not None, (
|
|
114
106
|
"Workflow logic not defined. Please use the @vibecore.workflow() decorator."
|
|
115
107
|
)
|
|
116
|
-
|
|
108
|
+
result = None
|
|
109
|
+
while user_message := await self._user_input():
|
|
110
|
+
result = await self.vibecore.workflow_logic(self, user_message)
|
|
111
|
+
|
|
112
|
+
assert result, "No result available after inputs exhausted."
|
|
113
|
+
return result
|
|
117
114
|
|
|
118
115
|
|
|
119
|
-
class VibecoreStaticRunner(
|
|
120
|
-
def __init__(
|
|
121
|
-
|
|
116
|
+
class VibecoreStaticRunner(VibecoreRunner[TContext, TWorkflowReturn]):
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
vibecore: "Vibecore[TContext, TWorkflowReturn]",
|
|
120
|
+
context: TContext | None = None,
|
|
121
|
+
session: Session | None = None,
|
|
122
|
+
) -> None:
|
|
123
|
+
super().__init__(vibecore, context=context, session=session)
|
|
122
124
|
self.inputs: list[str] = []
|
|
123
125
|
self.prints: list[str] = []
|
|
124
126
|
|
|
125
|
-
async def user_input(self, prompt: str = "") -> str:
|
|
126
|
-
assert self.inputs, "No more user inputs available."
|
|
127
|
-
return self.inputs.pop()
|
|
128
|
-
|
|
129
127
|
async def print(self, message: str) -> None:
|
|
130
128
|
# Capture printed messages instead of displaying them
|
|
131
129
|
self.prints.append(message)
|
|
@@ -136,21 +134,29 @@ class VibecoreStaticRunner(VibecoreSimpleRunner[TWorkflowReturn]):
|
|
|
136
134
|
assert self.vibecore.workflow_logic is not None, (
|
|
137
135
|
"Workflow logic not defined. Please use the @vibecore.workflow() decorator."
|
|
138
136
|
)
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
result = None
|
|
138
|
+
for user_message in inputs:
|
|
139
|
+
result = await self.vibecore.workflow_logic(self, user_message)
|
|
141
140
|
|
|
141
|
+
assert result, "No result available after inputs exhausted."
|
|
142
|
+
return result
|
|
142
143
|
|
|
143
|
-
class VibecoreTextualRunner(VibecoreRunnerBase[TWorkflowReturn]):
|
|
144
|
-
def __init__(self, vibecore: "Vibecore[TWorkflowReturn]") -> None:
|
|
145
|
-
super().__init__(vibecore)
|
|
146
|
-
self.app = VibecoreApp(self.vibecore.context, self.vibecore.starting_agent, show_welcome=False)
|
|
147
|
-
self.app_ready_event = asyncio.Event()
|
|
148
144
|
|
|
149
|
-
|
|
150
|
-
def
|
|
151
|
-
|
|
145
|
+
class VibecoreTextualRunner(VibecoreRunner[AppAwareContext, TWorkflowReturn]):
|
|
146
|
+
def __init__(
|
|
147
|
+
self,
|
|
148
|
+
vibecore: "Vibecore[AppAwareContext, TWorkflowReturn]",
|
|
149
|
+
context: AppAwareContext | None = None,
|
|
150
|
+
session: Session | None = None,
|
|
151
|
+
) -> None:
|
|
152
|
+
super().__init__(vibecore, context=context, session=session)
|
|
153
|
+
self.app = VibecoreApp(
|
|
154
|
+
self,
|
|
155
|
+
show_welcome=False,
|
|
156
|
+
)
|
|
157
|
+
self.app_ready_event = asyncio.Event()
|
|
152
158
|
|
|
153
|
-
async def
|
|
159
|
+
async def _user_input(self, prompt: str = "") -> str:
|
|
154
160
|
if prompt:
|
|
155
161
|
await self.print(prompt)
|
|
156
162
|
self.app.query_one(MyTextArea).disabled = False
|
|
@@ -165,12 +171,12 @@ class VibecoreTextualRunner(VibecoreRunnerBase[TWorkflowReturn]):
|
|
|
165
171
|
|
|
166
172
|
async def run_agent(
|
|
167
173
|
self,
|
|
168
|
-
starting_agent: Agent[
|
|
174
|
+
starting_agent: Agent[AppAwareContext],
|
|
169
175
|
input: str | list[TResponseInputItem],
|
|
170
176
|
*,
|
|
171
|
-
context:
|
|
177
|
+
context: AppAwareContext | None = None,
|
|
172
178
|
max_turns: int = DEFAULT_MAX_TURNS,
|
|
173
|
-
hooks: RunHooks[
|
|
179
|
+
hooks: RunHooks[AppAwareContext] | None = None,
|
|
174
180
|
run_config: RunConfig | None = None,
|
|
175
181
|
previous_response_id: str | None = None,
|
|
176
182
|
session: Session | None = None,
|
|
@@ -200,7 +206,6 @@ class VibecoreTextualRunner(VibecoreRunnerBase[TWorkflowReturn]):
|
|
|
200
206
|
Args:
|
|
201
207
|
app: App to run.
|
|
202
208
|
"""
|
|
203
|
-
|
|
204
209
|
with self.app._context():
|
|
205
210
|
try:
|
|
206
211
|
self.app._loop = asyncio.get_running_loop()
|
|
@@ -216,15 +221,19 @@ class VibecoreTextualRunner(VibecoreRunnerBase[TWorkflowReturn]):
|
|
|
216
221
|
assert self.vibecore.workflow_logic is not None, (
|
|
217
222
|
"Workflow logic not defined. Please use the @vibecore.workflow() decorator."
|
|
218
223
|
)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
224
|
+
while True:
|
|
225
|
+
user_message = await self._user_input()
|
|
226
|
+
result = await self.vibecore.workflow_logic(self, user_message)
|
|
227
|
+
assert result, "No result available after inputs exhausted."
|
|
228
|
+
return result
|
|
223
229
|
|
|
224
|
-
async def run(self, shutdown: bool = False) -> TWorkflowReturn:
|
|
225
|
-
|
|
230
|
+
async def run(self, inputs: list[str] | None = None, shutdown: bool = False) -> TWorkflowReturn:
|
|
231
|
+
if inputs:
|
|
232
|
+
self.app.message_queue.extend(inputs)
|
|
226
233
|
app_task = asyncio.create_task(self._run_app(), name=f"run_app({self.app})")
|
|
227
234
|
await self.app_ready_event.wait()
|
|
235
|
+
|
|
236
|
+
await self.app.load_session_history(self.session)
|
|
228
237
|
pilot = Pilot(self.app)
|
|
229
238
|
logic_task: asyncio.Task[TWorkflowReturn] | None = None
|
|
230
239
|
|
|
@@ -249,34 +258,29 @@ class VibecoreTextualRunner(VibecoreRunnerBase[TWorkflowReturn]):
|
|
|
249
258
|
self.app.exit()
|
|
250
259
|
await app_task
|
|
251
260
|
else:
|
|
261
|
+
await self.print("Workflow complete. Press Ctrl-Q to exit.")
|
|
252
262
|
# Enable text input so users can interact freely
|
|
253
263
|
self.app.query_one(MyTextArea).disabled = False
|
|
254
264
|
# Wait until app is exited
|
|
255
265
|
await app_task
|
|
256
266
|
return result
|
|
257
|
-
|
|
258
|
-
raise RuntimeError("Unexpected state: both tasks completed")
|
|
267
|
+
raise AssertionError(f"Unexpected state: done={done}, pending={pending}")
|
|
259
268
|
|
|
260
269
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
self.starting_agent = starting_agent
|
|
266
|
-
self.disable_user_input = disable_user_input
|
|
267
|
-
self.runner: VibecoreRunnerBase[TWorkflowReturn] = VibecoreRunnerBase(self)
|
|
268
|
-
|
|
269
|
-
@property
|
|
270
|
-
def session(self) -> Session:
|
|
271
|
-
return self.runner.session
|
|
270
|
+
WorkflowLogic: TypeAlias = Callable[
|
|
271
|
+
Concatenate[VibecoreRunner[TContext, TWorkflowReturn], str, ...],
|
|
272
|
+
Coroutine[Any, Any, TWorkflowReturn],
|
|
273
|
+
]
|
|
272
274
|
|
|
273
|
-
async def user_input(self, prompt: str = "") -> str:
|
|
274
|
-
return await self.runner.user_input(prompt)
|
|
275
275
|
|
|
276
|
-
|
|
277
|
-
|
|
276
|
+
class Vibecore(Generic[TContext, TWorkflowReturn]):
|
|
277
|
+
def __init__(self, disable_user_input: bool = True) -> None:
|
|
278
|
+
self.workflow_logic: WorkflowLogic[TContext, TWorkflowReturn] | None = None
|
|
279
|
+
self.disable_user_input = disable_user_input
|
|
278
280
|
|
|
279
|
-
def workflow(
|
|
281
|
+
def workflow(
|
|
282
|
+
self,
|
|
283
|
+
) -> Callable[[WorkflowLogic[TContext, TWorkflowReturn]], WorkflowLogic[TContext, TWorkflowReturn]]:
|
|
280
284
|
"""Decorator to define the workflow logic for the app.
|
|
281
285
|
|
|
282
286
|
Returns:
|
|
@@ -284,62 +288,80 @@ class Vibecore(Generic[TWorkflowReturn]):
|
|
|
284
288
|
"""
|
|
285
289
|
|
|
286
290
|
def decorator(
|
|
287
|
-
func:
|
|
288
|
-
) ->
|
|
291
|
+
func: WorkflowLogic[TContext, TWorkflowReturn],
|
|
292
|
+
) -> WorkflowLogic[TContext, TWorkflowReturn]:
|
|
289
293
|
self.workflow_logic = func
|
|
290
294
|
return func
|
|
291
295
|
|
|
292
296
|
return decorator
|
|
293
297
|
|
|
294
|
-
|
|
298
|
+
@overload
|
|
299
|
+
async def run_textual(
|
|
295
300
|
self,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
*,
|
|
299
|
-
context: TContext | None = None,
|
|
300
|
-
max_turns: int = DEFAULT_MAX_TURNS,
|
|
301
|
-
hooks: RunHooks[TContext] | None = None,
|
|
302
|
-
run_config: RunConfig | None = None,
|
|
303
|
-
previous_response_id: str | None = None,
|
|
301
|
+
inputs: str | None = None,
|
|
302
|
+
context: AppAwareContext | None = None,
|
|
304
303
|
session: Session | None = None,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
304
|
+
shutdown: bool = False,
|
|
305
|
+
) -> TWorkflowReturn: ...
|
|
306
|
+
@overload
|
|
307
|
+
async def run_textual(
|
|
308
|
+
self,
|
|
309
|
+
inputs: list[str] | None = None,
|
|
310
|
+
context: AppAwareContext | None = None,
|
|
311
|
+
session: Session | None = None,
|
|
312
|
+
shutdown: bool = False,
|
|
313
|
+
) -> TWorkflowReturn: ...
|
|
314
|
+
|
|
315
|
+
async def run_textual(
|
|
316
|
+
self,
|
|
317
|
+
inputs: str | list[str] | None = None,
|
|
318
|
+
context: AppAwareContext | None = None,
|
|
319
|
+
session: Session | None = None,
|
|
320
|
+
shutdown: bool = False,
|
|
321
|
+
) -> TWorkflowReturn:
|
|
322
|
+
if isinstance(inputs, str):
|
|
323
|
+
inputs = [inputs]
|
|
316
324
|
|
|
317
|
-
async def run_textual(self, shutdown: bool = False) -> TWorkflowReturn:
|
|
318
325
|
if self.workflow_logic is None:
|
|
319
326
|
raise ValueError("Workflow logic not defined. Please use the @vibecore.workflow() decorator.")
|
|
320
327
|
|
|
321
|
-
|
|
322
|
-
|
|
328
|
+
assert isinstance(context, AppAwareContext) or context is None, (
|
|
329
|
+
"Textual runner requires AppAwareContext or None."
|
|
330
|
+
)
|
|
331
|
+
# Type checker needs help: after the assertion, we know context is AppAwareContext | None
|
|
332
|
+
# and this Vibecore instance can be treated as Vibecore[AppAwareContext, TWorkflowReturn]
|
|
333
|
+
runner = VibecoreTextualRunner(
|
|
334
|
+
cast("Vibecore[AppAwareContext, TWorkflowReturn]", self),
|
|
335
|
+
context=context,
|
|
336
|
+
session=session,
|
|
337
|
+
)
|
|
338
|
+
return await runner.run(inputs=inputs, shutdown=shutdown)
|
|
323
339
|
|
|
324
|
-
async def run_cli(self) -> TWorkflowReturn:
|
|
340
|
+
async def run_cli(self, context: TContext | None = None, session: Session | None = None) -> TWorkflowReturn:
|
|
325
341
|
if self.workflow_logic is None:
|
|
326
342
|
raise ValueError("Workflow logic not defined. Please use the @vibecore.workflow() decorator.")
|
|
327
343
|
|
|
328
|
-
|
|
329
|
-
return await
|
|
344
|
+
runner = VibecoreCliRunner(self, context=context, session=session)
|
|
345
|
+
return await runner.run()
|
|
330
346
|
|
|
331
347
|
@overload
|
|
332
|
-
async def run(
|
|
348
|
+
async def run(
|
|
349
|
+
self, inputs: str, context: TContext | None = None, session: Session | None = None
|
|
350
|
+
) -> TWorkflowReturn: ...
|
|
333
351
|
|
|
334
352
|
@overload
|
|
335
|
-
async def run(
|
|
353
|
+
async def run(
|
|
354
|
+
self, inputs: list[str], context: TContext | None = None, session: Session | None = None
|
|
355
|
+
) -> TWorkflowReturn: ...
|
|
336
356
|
|
|
337
|
-
async def run(
|
|
357
|
+
async def run(
|
|
358
|
+
self, inputs: str | list[str], context: TContext | None = None, session: Session | None = None
|
|
359
|
+
) -> TWorkflowReturn:
|
|
338
360
|
if isinstance(inputs, str):
|
|
339
361
|
inputs = [inputs]
|
|
340
362
|
|
|
341
363
|
if self.workflow_logic is None:
|
|
342
364
|
raise ValueError("Workflow logic not defined. Please use the @vibecore.workflow() decorator.")
|
|
343
365
|
|
|
344
|
-
|
|
345
|
-
return await
|
|
366
|
+
runner = VibecoreStaticRunner(self, context=context, session=session)
|
|
367
|
+
return await runner.run(inputs=inputs)
|
|
@@ -4,8 +4,6 @@ import json
|
|
|
4
4
|
from typing import Protocol
|
|
5
5
|
|
|
6
6
|
from agents import (
|
|
7
|
-
Agent,
|
|
8
|
-
AgentUpdatedStreamEvent,
|
|
9
7
|
MessageOutputItem,
|
|
10
8
|
RawResponsesStreamEvent,
|
|
11
9
|
RunItemStreamEvent,
|
|
@@ -43,10 +41,6 @@ class MessageHandler(Protocol):
|
|
|
43
41
|
"""Add a message to the widget's message list."""
|
|
44
42
|
...
|
|
45
43
|
|
|
46
|
-
async def handle_agent_update(self, new_agent: Agent) -> None:
|
|
47
|
-
"""Handle agent updates."""
|
|
48
|
-
...
|
|
49
|
-
|
|
50
44
|
async def handle_agent_error(self, error: Exception) -> None:
|
|
51
45
|
"""Handle errors during streaming."""
|
|
52
46
|
...
|
|
@@ -205,10 +199,6 @@ class AgentStreamHandler:
|
|
|
205
199
|
case MessageOutputItem():
|
|
206
200
|
await self.handle_message_complete()
|
|
207
201
|
|
|
208
|
-
case AgentUpdatedStreamEvent(new_agent=new_agent):
|
|
209
|
-
# log(f"AgentUpdatedStreamEvent new_agent: {new_agent.name}")
|
|
210
|
-
await self.message_handler.handle_agent_update(new_agent)
|
|
211
|
-
|
|
212
202
|
async def handle_task_tool_event(self, tool_name: str, tool_call_id: str, event: StreamEvent) -> None:
|
|
213
203
|
"""Handle streaming events from task tool sub-agents.
|
|
214
204
|
|