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 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.6
2
- alembic~=1.14.0
3
- sqlmodel~=0.0.22
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.5
6
+ Jinja2~=3.1.6
7
7
  python-jose~=3.4.0
8
8
  passlib~=1.7.4
9
9
 
10
- pytest~=8.3.4
11
- pytest-asyncio~=0.24.0
12
- pytest-cov~=6.0.0
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
- view_path = os.path.join(
13
- os.path.dirname(os.path.dirname(__file__)), "module", "gateway", "view"
14
- )
15
- assert response.text == render_content("homepage.html").body.decode("utf-8")
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
- async with agent.iter(
143
- user_prompt=user_prompt,
144
- message_history=ModelMessagesTypeAdapter.validate_python(history),
145
- ) as agent_run:
146
- async for node in agent_run:
147
- # Each node represents a step in the agent's execution
148
- await self._print_node(ctx, agent_run, node)
149
- new_history = json.loads(agent_run.result.all_messages_json())
150
- await self._write_conversation_history(ctx, new_history)
151
- return agent_run.result.data
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
- # Fixing anthrophic claude when call function with empty parameter
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
- @functools.wraps(func)
334
- async def wrapper(*args, **kwargs):
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
- return await run_async(func(*args, **kwargs))
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
- # Optionally, you can include more details from traceback if needed.
339
- error_details = traceback.format_exc()
340
- return f"Error: {e}\nDetails: {error_details}"
341
-
342
- return wrapper
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.1
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: autopep8 (>=2.0.4,<3.0.0)
20
- Requires-Dist: beautifulsoup4 (>=4.12.3,<5.0.0)
21
- Requires-Dist: black (>=24.10.0,<24.11.0)
22
- Requires-Dist: chromadb (>=0.5.20,<0.6.0) ; extra == "rag" or extra == "all"
23
- Requires-Dist: fastapi[standard] (>=0.115.6,<0.116.0)
24
- Requires-Dist: isort (>=5.13.2,<5.14.0)
25
- Requires-Dist: libcst (>=1.5.0,<2.0.0)
26
- Requires-Dist: openai (>=1.10.0,<2.0.0) ; extra == "rag" or extra == "all"
27
- Requires-Dist: pdfplumber (>=0.11.4,<0.12.0) ; extra == "rag" or extra == "all"
28
- Requires-Dist: playwright (>=1.43.0,<2.0.0) ; extra == "playwright" or extra == "all"
29
- Requires-Dist: psutil (>=6.1.1,<7.0.0)
30
- Requires-Dist: pydantic-ai (>=0.0.42,<0.0.43)
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=MvAGzoM3ElJZOPMKNqaTdnrT9PXi9Saq8CPa11RiLQk,1748
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=VqHI89r0E8sU5vp3h-AXrZy30vGWVP2E8MYsvVxQ_Wg,194
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=fJGnHGXe893idQ94-0Yd50YfxQRQZq3bLTd2Rgq2d-A,498
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=oPLruaSCqEF2fX5xtXfXflVYMknT2U5nBqC0CObT-ug,14282
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.1.dist-info/METADATA,sha256=wO-YpDHeIeoVT37roX4fGmsyN0XwRWSQOESYwpY9HTg,8515
345
- zrb-1.5.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
346
- zrb-1.5.1.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
347
- zrb-1.5.1.dist-info/RECORD,,
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