zrb 1.5.1__py3-none-any.whl → 1.5.2__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.
- zrb/__main__.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +7 -7
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +8 -7
- zrb/task/llm_task.py +136 -20
- {zrb-1.5.1.dist-info → zrb-1.5.2.dist-info}/METADATA +13 -14
- {zrb-1.5.1.dist-info → zrb-1.5.2.dist-info}/RECORD +8 -8
- {zrb-1.5.1.dist-info → zrb-1.5.2.dist-info}/WHEEL +0 -0
- {zrb-1.5.1.dist-info → zrb-1.5.2.dist-info}/entry_points.txt +0 -0
zrb/__main__.py
CHANGED
@@ -16,7 +16,7 @@ def serve_cli():
|
|
16
16
|
zrb_init_path_list = _get_zrb_init_path_list()
|
17
17
|
# load init scripts
|
18
18
|
for init_script in INIT_SCRIPTS:
|
19
|
-
abs_init_script = os.path.abspath(init_script)
|
19
|
+
abs_init_script = os.path.abspath(os.path.expanduser(init_script))
|
20
20
|
if abs_init_script not in zrb_init_path_list:
|
21
21
|
load_file(abs_init_script, -1)
|
22
22
|
# load zrb init
|
@@ -1,12 +1,12 @@
|
|
1
|
-
fastapi[standard]~=0.115.
|
2
|
-
alembic~=1.
|
3
|
-
sqlmodel~=0.0.
|
1
|
+
fastapi[standard]~=0.115.12
|
2
|
+
alembic~=1.15.2
|
3
|
+
sqlmodel~=0.0.24
|
4
4
|
ulid-py~=1.1.0
|
5
5
|
passlib~=1.7.4
|
6
|
-
Jinja2~=3.1.
|
6
|
+
Jinja2~=3.1.6
|
7
7
|
python-jose~=3.4.0
|
8
8
|
passlib~=1.7.4
|
9
9
|
|
10
|
-
pytest~=8.3.
|
11
|
-
pytest-asyncio~=0.
|
12
|
-
pytest-cov~=6.
|
10
|
+
pytest~=8.3.5
|
11
|
+
pytest-asyncio~=0.26.0
|
12
|
+
pytest-cov~=6.1.0
|
@@ -1,15 +1,16 @@
|
|
1
|
-
import os
|
2
|
-
|
3
1
|
from fastapi.testclient import TestClient
|
4
2
|
from my_app_name.main import app
|
3
|
+
from my_app_name.module.auth.service.user.user_service_factory import user_service
|
5
4
|
from my_app_name.module.gateway.util.view import render_content
|
6
5
|
|
7
6
|
|
8
|
-
def test_homepage():
|
7
|
+
async def test_homepage():
|
8
|
+
current_user = await user_service.get_current_user(access_token=None)
|
9
9
|
client = TestClient(app, base_url="http://localhost")
|
10
10
|
response = client.get("/")
|
11
11
|
assert response.status_code == 200
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
assert response.text == render_content(
|
13
|
+
"homepage.html",
|
14
|
+
page_name="gateway.home",
|
15
|
+
current_user=current_user,
|
16
|
+
).body.decode("utf-8")
|
zrb/task/llm_task.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import copy
|
1
2
|
import functools
|
2
3
|
import inspect
|
3
4
|
import json
|
@@ -6,6 +7,7 @@ import traceback
|
|
6
7
|
from collections.abc import Callable
|
7
8
|
from typing import Any
|
8
9
|
|
10
|
+
from openai import APIError
|
9
11
|
from pydantic_ai import Agent, Tool
|
10
12
|
from pydantic_ai.messages import (
|
11
13
|
FinalResultEvent,
|
@@ -139,16 +141,31 @@ class LLMTask(BaseTask):
|
|
139
141
|
history = await self._read_conversation_history(ctx)
|
140
142
|
user_prompt = self._get_message(ctx)
|
141
143
|
agent = self._get_agent(ctx)
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
144
|
+
try:
|
145
|
+
async with agent.iter(
|
146
|
+
user_prompt=user_prompt,
|
147
|
+
message_history=ModelMessagesTypeAdapter.validate_python(history),
|
148
|
+
) as agent_run:
|
149
|
+
async for node in agent_run:
|
150
|
+
# Each node represents a step in the agent's execution
|
151
|
+
# Reference: https://ai.pydantic.dev/agents/#streaming
|
152
|
+
try:
|
153
|
+
await self._print_node(ctx, agent_run, node)
|
154
|
+
except APIError as e:
|
155
|
+
# Extract detailed error information from the response
|
156
|
+
error_details = _extract_api_error_details(e)
|
157
|
+
ctx.log_error(f"API Error: {error_details}")
|
158
|
+
raise
|
159
|
+
except Exception as e:
|
160
|
+
ctx.log_error(f"Error processing node: {str(e)}")
|
161
|
+
ctx.log_error(f"Error type: {type(e).__name__}")
|
162
|
+
raise
|
163
|
+
new_history = json.loads(agent_run.result.all_messages_json())
|
164
|
+
await self._write_conversation_history(ctx, new_history)
|
165
|
+
return agent_run.result.data
|
166
|
+
except Exception as e:
|
167
|
+
ctx.log_error(f"Error in agent execution: {str(e)}")
|
168
|
+
raise
|
152
169
|
|
153
170
|
async def _print_node(self, ctx: AnyContext, agent_run: Any, node: Any):
|
154
171
|
if Agent.is_user_prompt_node(node):
|
@@ -204,9 +221,20 @@ class LLMTask(BaseTask):
|
|
204
221
|
async with node.stream(agent_run.ctx) as handle_stream:
|
205
222
|
async for event in handle_stream:
|
206
223
|
if isinstance(event, FunctionToolCallEvent):
|
207
|
-
#
|
208
|
-
if event.part.args == "":
|
224
|
+
# Handle empty arguments across different providers
|
225
|
+
if event.part.args == "" or event.part.args is None:
|
226
|
+
event.part.args = {}
|
227
|
+
elif isinstance(
|
228
|
+
event.part.args, str
|
229
|
+
) and event.part.args.strip() in ["null", "{}"]:
|
230
|
+
# Some providers might send "null" or "{}" as a string
|
209
231
|
event.part.args = {}
|
232
|
+
# Handle dummy property if present (from our schema sanitization)
|
233
|
+
if (
|
234
|
+
isinstance(event.part.args, dict)
|
235
|
+
and "_dummy" in event.part.args
|
236
|
+
):
|
237
|
+
del event.part.args["_dummy"]
|
210
238
|
ctx.print(
|
211
239
|
stylize_faint(
|
212
240
|
f"[Tools] The LLM calls tool={event.part.tool_name!r} with args={event.part.args} (tool_call_id={event.part.tool_call_id!r})" # noqa
|
@@ -330,13 +358,101 @@ class LLMTask(BaseTask):
|
|
330
358
|
|
331
359
|
|
332
360
|
def _wrap_tool(func):
|
333
|
-
|
334
|
-
|
361
|
+
sig = inspect.signature(func)
|
362
|
+
if len(sig.parameters) == 0:
|
363
|
+
|
364
|
+
@functools.wraps(func)
|
365
|
+
async def wrapper(_dummy=None):
|
366
|
+
try:
|
367
|
+
return await run_async(func())
|
368
|
+
except Exception as e:
|
369
|
+
# Optionally, you can include more details from traceback if needed.
|
370
|
+
error_details = traceback.format_exc()
|
371
|
+
return f"Error: {e}\nDetails: {error_details}"
|
372
|
+
|
373
|
+
new_sig = inspect.Signature(
|
374
|
+
parameters=[
|
375
|
+
inspect.Parameter(
|
376
|
+
"_dummy", inspect.Parameter.POSITIONAL_OR_KEYWORD, default=None
|
377
|
+
)
|
378
|
+
]
|
379
|
+
)
|
380
|
+
# Override the wrapper's signature so introspection yields a non-empty schema.
|
381
|
+
wrapper.__signature__ = new_sig
|
382
|
+
return wrapper
|
383
|
+
else:
|
384
|
+
|
385
|
+
@functools.wraps(func)
|
386
|
+
async def wrapper(*args, **kwargs):
|
387
|
+
try:
|
388
|
+
return await run_async(func(*args, **kwargs))
|
389
|
+
except Exception as e:
|
390
|
+
# Optionally, you can include more details from traceback if needed.
|
391
|
+
error_details = traceback.format_exc()
|
392
|
+
return f"Error: {e}\nDetails: {error_details}"
|
393
|
+
|
394
|
+
return wrapper
|
395
|
+
|
396
|
+
|
397
|
+
def _extract_api_error_details(error: APIError) -> str:
|
398
|
+
"""Extract detailed error information from an APIError."""
|
399
|
+
details = f"{error.message}"
|
400
|
+
# Try to parse the error body as JSON
|
401
|
+
if error.body:
|
335
402
|
try:
|
336
|
-
|
403
|
+
if isinstance(error.body, str):
|
404
|
+
body_json = json.loads(error.body)
|
405
|
+
elif isinstance(error.body, bytes):
|
406
|
+
body_json = json.loads(error.body.decode("utf-8"))
|
407
|
+
else:
|
408
|
+
body_json = error.body
|
409
|
+
# Extract error details from the JSON structure
|
410
|
+
if isinstance(body_json, dict):
|
411
|
+
if "error" in body_json:
|
412
|
+
error_obj = body_json["error"]
|
413
|
+
if isinstance(error_obj, dict):
|
414
|
+
if "message" in error_obj:
|
415
|
+
details += f"\nProvider message: {error_obj['message']}"
|
416
|
+
if "code" in error_obj:
|
417
|
+
details += f"\nError code: {error_obj['code']}"
|
418
|
+
if "status" in error_obj:
|
419
|
+
details += f"\nStatus: {error_obj['status']}"
|
420
|
+
# Check for metadata that might contain provider-specific information
|
421
|
+
if "metadata" in body_json and isinstance(body_json["metadata"], dict):
|
422
|
+
metadata = body_json["metadata"]
|
423
|
+
if "provider_name" in metadata:
|
424
|
+
details += f"\nProvider: {metadata['provider_name']}"
|
425
|
+
if "raw" in metadata:
|
426
|
+
try:
|
427
|
+
raw_json = json.loads(metadata["raw"])
|
428
|
+
if "error" in raw_json and isinstance(
|
429
|
+
raw_json["error"], dict
|
430
|
+
):
|
431
|
+
raw_error = raw_json["error"]
|
432
|
+
if "message" in raw_error:
|
433
|
+
details += (
|
434
|
+
f"\nRaw error message: {raw_error['message']}"
|
435
|
+
)
|
436
|
+
except (KeyError, TypeError, ValueError):
|
437
|
+
# If we can't parse the raw JSON, just include it as is
|
438
|
+
details += f"\nRaw error data: {metadata['raw']}"
|
439
|
+
except json.JSONDecodeError:
|
440
|
+
# If we can't parse the JSON, include the raw body
|
441
|
+
details += f"\nRaw error body: {error.body}"
|
337
442
|
except Exception as e:
|
338
|
-
#
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
443
|
+
# Catch any other exceptions during parsing
|
444
|
+
details += f"\nError parsing error body: {str(e)}"
|
445
|
+
# Include request information if available
|
446
|
+
if hasattr(error, "request") and error.request:
|
447
|
+
if hasattr(error.request, "method") and hasattr(error.request, "url"):
|
448
|
+
details += f"\nRequest: {error.request.method} {error.request.url}"
|
449
|
+
# Include a truncated version of the request content if available
|
450
|
+
if hasattr(error.request, "content") and error.request.content:
|
451
|
+
content = error.request.content
|
452
|
+
if isinstance(content, bytes):
|
453
|
+
try:
|
454
|
+
content = content.decode("utf-8")
|
455
|
+
except UnicodeDecodeError:
|
456
|
+
content = str(content)
|
457
|
+
details += f"\nRequest content: {content}"
|
458
|
+
return details
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: zrb
|
3
|
-
Version: 1.5.
|
3
|
+
Version: 1.5.2
|
4
4
|
Summary: Your Automation Powerhouse
|
5
5
|
Home-page: https://github.com/state-alchemists/zrb
|
6
6
|
License: AGPL-3.0-or-later
|
@@ -16,19 +16,18 @@ Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Provides-Extra: all
|
17
17
|
Provides-Extra: playwright
|
18
18
|
Provides-Extra: rag
|
19
|
-
Requires-Dist:
|
20
|
-
Requires-Dist:
|
21
|
-
Requires-Dist:
|
22
|
-
Requires-Dist:
|
23
|
-
Requires-Dist:
|
24
|
-
Requires-Dist:
|
25
|
-
Requires-Dist:
|
26
|
-
Requires-Dist:
|
27
|
-
Requires-Dist:
|
28
|
-
Requires-Dist:
|
29
|
-
Requires-Dist:
|
30
|
-
Requires-Dist:
|
31
|
-
Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
|
19
|
+
Requires-Dist: beautifulsoup4 (>=4.13.3,<5.0.0)
|
20
|
+
Requires-Dist: black (>=25.1.0,<25.2.0)
|
21
|
+
Requires-Dist: chromadb (>=0.6.3,<0.7.0) ; extra == "rag" or extra == "all"
|
22
|
+
Requires-Dist: fastapi[standard] (>=0.115.12,<0.116.0)
|
23
|
+
Requires-Dist: isort (>=6.0.1,<6.1.0)
|
24
|
+
Requires-Dist: libcst (>=1.7.0,<2.0.0)
|
25
|
+
Requires-Dist: openai (>=1.70.0,<2.0.0) ; extra == "rag" or extra == "all"
|
26
|
+
Requires-Dist: pdfplumber (>=0.11.6,<0.12.0) ; extra == "rag" or extra == "all"
|
27
|
+
Requires-Dist: playwright (>=1.51.0,<2.0.0) ; extra == "playwright" or extra == "all"
|
28
|
+
Requires-Dist: psutil (>=7.0.0,<8.0.0)
|
29
|
+
Requires-Dist: pydantic-ai (>=0.0.49,<0.0.50)
|
30
|
+
Requires-Dist: python-dotenv (>=1.1.0,<2.0.0)
|
32
31
|
Requires-Dist: python-jose[cryptography] (>=3.4.0,<4.0.0)
|
33
32
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
34
33
|
Requires-Dist: ulid-py (>=1.1.0,<2.0.0)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
zrb/__init__.py,sha256=1waPjZcA3IHUEvIuVQso0YfNfW9i7SCJgEfzhiNTaCk,3020
|
2
|
-
zrb/__main__.py,sha256=
|
2
|
+
zrb/__main__.py,sha256=QJfEeJ-fsuxxb4md1BuAt7CX4jJmb8ttHf16bb2bf40,1768
|
3
3
|
zrb/attr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
zrb/attr/type.py,sha256=4TV5gPYMMrKh5V-yB6iRYKCbsXAH_AvGXMsjxKLHcUs,568
|
5
5
|
zrb/builtin/__init__.py,sha256=oXG4Zm_rIp3G81Y7hiSe38jeS2sGZAnADoP_yxxhYEc,1926
|
@@ -162,7 +162,7 @@ zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view
|
|
162
162
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.yellow.min.css,sha256=mOkvNiE272UAgO8cVewJjU9BYidLxDZ97IcYodW--Ik,82218
|
163
163
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.zinc.min.css,sha256=s8SdVQv4fX3W0LbfKDgUDczWcCyUfRbZhpyYtLdASC0,82212
|
164
164
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html,sha256=Lg4vONCLOx8PSORFitg8JZa-4dp-pyUvKSVzCJEFsB8,4048
|
165
|
-
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt,sha256=
|
165
|
+
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt,sha256=jSLXX-ZOElSG69qjx5yVcpg6X_QJoisNDUYpCv2ldH8,195
|
166
166
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
167
167
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py,sha256=q66LXdZ-QTb30F1VTXNLnjyYBlK_ThVLiHgavtJy4LY,1424
|
168
168
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py,sha256=7USbuhHhPc3xXkmwiqTVKsN8-eFWS8Q7emKxCGNGPw0,3244
|
@@ -175,7 +175,7 @@ zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permissio
|
|
175
175
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_update_permission.py,sha256=AHTUIX7cdR5tKkbxs6JVrp5uoeiwIvqaBrhLFGOYoFg,2498
|
176
176
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/test_user_session.py,sha256=RITk3KohVSEzdvyoOefzrx2eabHEhAx3eS3opjZwU5k,7096
|
177
177
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py,sha256=nqbRJtGQdaHsj3MqiFrUPpQteRBRPiyHp9dpdUZcMg0,823
|
178
|
-
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py,sha256=
|
178
|
+
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py,sha256=NnhAjs8JParlmTKynRb-l65h8FvEG_Tap5m8kDYvxOM,618
|
179
179
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_not_found_error.py,sha256=SBqFRgGUldUtIz-uMf9a5DeWDPFeKbhvnvwiYr-jjns,502
|
180
180
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test.sh,sha256=QIwe6InrlEkmxdtj6NTO9JozeECYN0O4vx-x-qid6go,148
|
181
181
|
zrb/builtin/project/add/fastapp/fastapp_util.py,sha256=LfKcb_daaAdhMz0BgbYd1yIvw7DQzruZ7bjB7gLGFdk,1533
|
@@ -300,7 +300,7 @@ zrb/task/base_task.py,sha256=SQRf37bylS586KwyW0eYDe9JZ5Hl18FP8kScHae6y3A,21251
|
|
300
300
|
zrb/task/base_trigger.py,sha256=jC722rDvodaBLeNaFghkTyv1u0QXrK6BLZUUqcmBJ7Q,4581
|
301
301
|
zrb/task/cmd_task.py,sha256=pUKRSR4DZKjbmluB6vi7cxqyhxOLfJ2czSpYeQbiDvo,10705
|
302
302
|
zrb/task/http_check.py,sha256=Gf5rOB2Se2EdizuN9rp65HpGmfZkGc-clIAlHmPVehs,2565
|
303
|
-
zrb/task/llm_task.py,sha256=
|
303
|
+
zrb/task/llm_task.py,sha256=di9lYvcwd3uCPe9KzbDsXNy2ZJT49DmIIuYUS_bd8DY,19942
|
304
304
|
zrb/task/make_task.py,sha256=PD3b_aYazthS8LHeJsLAhwKDEgdurQZpymJDKeN60u0,2265
|
305
305
|
zrb/task/rsync_task.py,sha256=GSL9144bmp6F0EckT6m-2a1xG25AzrrWYzH4k3SVUKM,6370
|
306
306
|
zrb/task/scaffolder.py,sha256=rME18w1HJUHXgi9eTYXx_T2G4JdqDYzBoNOkdOOo5-o,6806
|
@@ -341,7 +341,7 @@ zrb/util/string/name.py,sha256=8picJfUBXNpdh64GNaHv3om23QHhUZux7DguFLrXHp8,1163
|
|
341
341
|
zrb/util/todo.py,sha256=1nDdwPc22oFoK_1ZTXyf3638Bg6sqE2yp_U4_-frHoc,16015
|
342
342
|
zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
343
343
|
zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
|
344
|
-
zrb-1.5.
|
345
|
-
zrb-1.5.
|
346
|
-
zrb-1.5.
|
347
|
-
zrb-1.5.
|
344
|
+
zrb-1.5.2.dist-info/METADATA,sha256=sY3qVRuIZtiXKa-osPrsWzZ8Fa3rGHzz4NfBQFuuHP0,8470
|
345
|
+
zrb-1.5.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
346
|
+
zrb-1.5.2.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
|
347
|
+
zrb-1.5.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|