Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Patch instructions and canonical name #1056

Merged
merged 1 commit into from
Jan 14, 2025
Merged

Conversation

whiterabbit1983
Copy link
Contributor

@whiterabbit1983 whiterabbit1983 commented Jan 14, 2025

PR Type

Bug fix, Enhancement


Description

  • Added support for updating instructions and canonical_name in the patch_agent query.

  • Enhanced SQL query logic to handle new fields conditionally.

  • Updated function call to include new parameters for instructions and canonical_name.


Changes walkthrough 📝

Relevant files
Enhancement
patch_agent.py
Support `instructions` and `canonical_name` in patch_agent

agents-api/agents_api/queries/agents/patch_agent.py

  • Added conditional logic for instructions and canonical_name in SQL
    query.
  • Updated function parameters to include instructions and
    canonical_name.
  • Enhanced query to handle new fields when provided.
  • +10/-0   

    💡 PR-Agent usage: Comment /help "your question" on any pull request to receive relevant information


    Important

    Add instructions and canonical_name fields to agent update query in patch_agent.py.

    • SQL Query Update:
      • In patch_agent.py, added instructions and canonical_name fields to the SQL update query for agents.
      • Updated params list to include data.instructions and data.canonical_name.
    • Behavior:
      • Allows partial updates to instructions and canonical_name fields in the agents table based on provided data.

    This description was created by Ellipsis for 56fcfbf. It will automatically update as commits are pushed.

    Copy link
    Contributor

    qodo-merge-pro-for-open-source bot commented Jan 14, 2025

    CI Failure Feedback 🧐

    (Checks updated until commit 56fcfbf)

    Action: Test

    Failed stage: Run tests [❌]

    Failed test name: test_agent_routes

    Failure summary:

    The action failed due to a database connection pool configuration error. Specifically, the min_size
    (10) was set larger than the max_size (4) in the asyncpg connection pool settings, which is invalid.
    This caused the following test failures:

  • test_agent_routes: route: unauthorized should fail
  • test_agent_routes: route: create agent
  • test_agent_routes: route: create agent with instructions

    The root cause was a ValueError with message "min_size is greater than max_size" when initializing
    the connection pool.

  • Relevant error logs:
    1:  ##[group]Operating System
    2:  Ubuntu
    ...
    
    1336:  PASS  test_agent_queries:28 query: create agent sql                          1%
    1337:  PASS  test_agent_queries:44 query: create or update agent sql                2%
    1338:  PASS  test_agent_queries:63 query: update agent sql                          2%
    1339:  PASS  test_agent_queries:85 query: get agent not exists sql                  3%
    1340:  PASS  test_agent_queries:96 query: get agent exists sql                      3%
    1341:  PASS  test_agent_queries:111 query: list agents sql                          4%
    1342:  PASS  test_agent_queries:122 query: patch agent sql                          4%
    1343:  PASS  test_agent_queries:143 query: delete agent sql                         5%
    1344:  FAIL  test_agent_routes:9 route: unauthorized should fail                    6%
    1345:  FAIL  test_agent_routes:26 route: create agent                               6%
    1346:  FAIL  test_agent_routes:43 route: create agent with instructions             7%
    1347:  ─────────────────────── route: unauthorized should fail ────────────────────────
    1348:  Failed at tests/test_agent_routes.py                                          
    ...
    
    1469:  │ │              name = '_dsn'                                           │ │  
    1470:  │ │              self = TestArgumentResolver(                            │ │  
    1471:  │ │                     │   test=Test(                                   │ │  
    1472:  │ │                     │   │   fn=<function _ at 0x7f034b196ca0>,       │ │  
    1473:  │ │                     │   │   module_name='test_agent_routes',         │ │  
    1474:  │ │                     │   │   id='5af32de549da4b6c8484592ceffa8b9a',   │ │  
    1475:  │ │                     │   │   marker=None,                             │ │  
    1476:  │ │                     │   │   description='route: unauthorized should  │ │  
    1477:  │ │                     fail',                                           │ │  
    ...
    
    1596:  │ │ self = <anyio._backends._asyncio.BlockingPortal object at            │ │  
    1597:  │ │        0x7f0349f94f80>                                               │ │  
    1598:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    1599:  │                                                                          │  
    1600:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    1601:  │ python3.12/concurrent/futures/_base.py:456 in result                     │  
    1602:  │                                                                          │  
    1603:  │   453 │   │   │   │   if self._state in [CANCELLED, CANCELLED_AND_NOTIFI │  
    1604:  │   454 │   │   │   │   │   raise CancelledError()                         │  
    1605:  │   455 │   │   │   │   elif self._state == FINISHED:                      │  
    1606:  │ ❱ 456 │   │   │   │   │   return self.__get_result()                     │  
    1607:  │   457 │   │   │   │   else:                                              │  
    1608:  │   458 │   │   │   │   │   raise TimeoutError()                           │  
    ...
    
    1640:  │   221 │   │   except self._cancelled_exc_class:                          │  
    1641:  │                                                                          │  
    1642:  │ ╭─────────────────────────────── locals ───────────────────────────────╮ │  
    1643:  │ │                args = ()                                             │ │  
    1644:  │ │                func = <bound method TestClient.wait_startup of       │ │  
    1645:  │ │                       <starlette.testclient.TestClient object at     │ │  
    1646:  │ │                       0x7f0349faaae0>>                               │ │  
    1647:  │ │              future = <Future at 0x7f034cd916a0 state=finished       │ │  
    1648:  │ │                       raised ValueError>                             │ │  
    ...
    
    1652:  │ │               scope = None                                           │ │  
    1653:  │ │                self = <anyio._backends._asyncio.BlockingPortal       │ │  
    1654:  │ │                       object at 0x7f0349f94f80>                      │ │  
    1655:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    1656:  │                                                                          │  
    1657:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    1658:  │ ges/starlette/testclient.py:774 in wait_startup                          │  
    1659:  │                                                                          │  
    1660:  │   771 │   │   │   "lifespan.startup.failed",                             │  
    1661:  │   772 │   │   )                                                          │  
    1662:  │   773 │   │   if message["type"] == "lifespan.startup.failed":           │  
    1663:  │ ❱ 774 │   │   │   await receive()                                        │  
    1664:  │   775 │                                                                  │  
    1665:  │   776 │   async def wait_shutdown(self) -> None:                         │  
    1666:  │   777 │   │   async def receive() -> typing.Any:                         │  
    1667:  │                                                                          │  
    1668:  │ ╭─────────────────────────────── locals ───────────────────────────────╮ │  
    1669:  │ │ message = {                                                          │ │  
    1670:  │ │           │   'type': 'lifespan.startup.failed',                     │ │  
    ...
    
    1689:  │ │ message = None                                                       │ │  
    1690:  │ │    self = <starlette.testclient.TestClient object at 0x7f0349faaae0> │ │  
    1691:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    1692:  │                                                                          │  
    1693:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    1694:  │ python3.12/concurrent/futures/_base.py:449 in result                     │  
    1695:  │                                                                          │  
    1696:  │   446 │   │   │   │   if self._state in [CANCELLED, CANCELLED_AND_NOTIFI │  
    1697:  │   447 │   │   │   │   │   raise CancelledError()                         │  
    ...
    
    1733:  │   221 │   │   except self._cancelled_exc_class:                          │  
    1734:  │                                                                          │  
    1735:  │ ╭─────────────────────────────── locals ───────────────────────────────╮ │  
    1736:  │ │                args = ()                                             │ │  
    1737:  │ │                func = <bound method TestClient.lifespan of           │ │  
    1738:  │ │                       <starlette.testclient.TestClient object at     │ │  
    1739:  │ │                       0x7f0349faaae0>>                               │ │  
    1740:  │ │              future = <Future at 0x7f0349f945f0 state=finished       │ │  
    1741:  │ │                       raised ValueError>                             │ │  
    ...
    
    1848:  │ │           waiting_senders=OrderedDict()), _closed=False),            │ │  
    1849:  │ │           receive_stream=MemoryObjectReceiveStream(_state=MemoryObj… │ │  
    1850:  │ │           buffer=deque([]), open_send_channels=1,                    │ │  
    1851:  │ │           open_receive_channels=1, waiting_receivers=OrderedDict(),  │ │  
    1852:  │ │           waiting_senders=OrderedDict()), _closed=False))>           │ │  
    1853:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    1854:  │                                                                          │  
    1855:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    1856:  │ ges/starlette/middleware/errors.py:152 in __call__                       │  
    ...
    
    1876:  │ │   scope = {                                                          │ │  
    1877:  │ │           │   'type': 'lifespan',                                    │ │  
    1878:  │ │           │   'state': {},                                           │ │  
    1879:  │ │           │   'app': <fastapi.applications.FastAPI object at         │ │  
    1880:  │ │           0x7f034cad9190>,                                           │ │  
    1881:  │ │           │   'router': <fastapi.routing.APIRouter object at         │ │  
    1882:  │ │           0x7f034cb6c620>                                            │ │  
    1883:  │ │           }                                                          │ │  
    1884:  │ │    self = <starlette.middleware.errors.ServerErrorMiddleware object  │ │  
    ...
    
    2106:  │ ges/starlette/routing.py:693 in lifespan                                 │  
    2107:  │                                                                          │  
    2108:  │   690 │   │   app: typing.Any = scope.get("app")                         │  
    2109:  │   691 │   │   await receive()                                            │  
    2110:  │   692 │   │   try:                                                       │  
    2111:  │ ❱ 693 │   │   │   async with self.lifespan_context(app) as maybe_state:  │  
    2112:  │   694 │   │   │   │   if maybe_state is not None:                        │  
    2113:  │   695 │   │   │   │   │   if "state" not in scope:                       │  
    2114:  │   696 │   │   │   │   │   │   raise RuntimeError('The server does not su │  
    ...
    
    2150:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2151:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2152:  │                                                                          │  
    2153:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2154:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2155:  │   209 │   │   try:                                                       │  
    2156:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2157:  │   211 │   │   except StopAsyncIteration:                                 │  
    2158:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2184:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2185:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2186:  │                                                                          │  
    2187:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2188:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2189:  │   209 │   │   try:                                                       │  
    2190:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2191:  │   211 │   │   except StopAsyncIteration:                                 │  
    2192:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2218:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2219:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2220:  │                                                                          │  
    2221:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2222:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2223:  │   209 │   │   try:                                                       │  
    2224:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2225:  │   211 │   │   except StopAsyncIteration:                                 │  
    2226:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2252:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2253:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2254:  │                                                                          │  
    2255:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2256:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2257:  │   209 │   │   try:                                                       │  
    2258:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2259:  │   211 │   │   except StopAsyncIteration:                                 │  
    2260:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2286:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2287:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2288:  │                                                                          │  
    2289:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2290:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2291:  │   209 │   │   try:                                                       │  
    2292:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2293:  │   211 │   │   except StopAsyncIteration:                                 │  
    2294:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2320:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2321:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2322:  │                                                                          │  
    2323:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2324:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2325:  │   209 │   │   try:                                                       │  
    2326:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2327:  │   211 │   │   except StopAsyncIteration:                                 │  
    2328:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2354:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2355:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2356:  │                                                                          │  
    2357:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2358:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2359:  │   209 │   │   try:                                                       │  
    2360:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2361:  │   211 │   │   except StopAsyncIteration:                                 │  
    2362:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2388:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2389:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2390:  │                                                                          │  
    2391:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2392:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2393:  │   209 │   │   try:                                                       │  
    2394:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2395:  │   211 │   │   except StopAsyncIteration:                                 │  
    2396:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2422:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2423:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2424:  │                                                                          │  
    2425:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2426:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2427:  │   209 │   │   try:                                                       │  
    2428:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2429:  │   211 │   │   except StopAsyncIteration:                                 │  
    2430:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2456:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2457:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2458:  │                                                                          │  
    2459:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2460:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2461:  │   209 │   │   try:                                                       │  
    2462:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2463:  │   211 │   │   except StopAsyncIteration:                                 │  
    2464:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2490:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    2491:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    2492:  │                                                                          │  
    2493:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    2494:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    2495:  │   209 │   │   try:                                                       │  
    2496:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    2497:  │   211 │   │   except StopAsyncIteration:                                 │  
    2498:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    2561:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    2562:  │                                                                          │  
    2563:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    2564:  │ ges/asyncpg/pool.py:361 in __init__                                      │  
    2565:  │                                                                          │  
    2566:  │    358 │   │   │   │   'min_size is expected to be greater or equal to z │  
    2567:  │    359 │   │                                                             │  
    2568:  │    360 │   │   if min_size > max_size:                                   │  
    2569:  │ ❱  361 │   │   │   raise ValueError('min_size is greater than max_size') │  
    2570:  │    362 │   │                                                             │  
    2571:  │    363 │   │   if max_queries <= 0:                                      │  
    2572:  │    364 │   │   │   raise ValueError('max_queries is expected to be great │  
    ...
    
    2586:  │ │                        max_size = 4                                  │ │  
    2587:  │ │                        min_size = 10                                 │ │  
    2588:  │ │                           reset = None                               │ │  
    2589:  │ │                            self = <asyncpg.pool.Pool object at       │ │  
    2590:  │ │                                   0x7f0349f0d3c0>                    │ │  
    2591:  │ │                           setup = None                               │ │  
    2592:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    2593:  ╰──────────────────────────────────────────────────────────────────────────╯  
    2594:  ValueError: min_size is greater than max_size                                 
    ...
    
    2671:  │ │         │   }                                                        │ │  
    2672:  │ │         )                                                            │ │  
    2673:  │ │  self = TestArgumentResolver(                                        │ │  
    2674:  │ │         │   test=Test(                                               │ │  
    2675:  │ │         │   │   fn=<function _ at 0x7f034b196ca0>,                   │ │  
    2676:  │ │         │   │   module_name='test_agent_routes',                     │ │  
    2677:  │ │         │   │   id='5af32de549da4b6c8484592ceffa8b9a',               │ │  
    2678:  │ │         │   │   marker=None,                                         │ │  
    2679:  │ │         │   │   description='route: unauthorized should fail',       │ │  
    ...
    
    2798:  │ │      resolved_args = {}                                              │ │  
    2799:  │ │               self = TestArgumentResolver(                           │ │  
    2800:  │ │                      │   test=Test(                                  │ │  
    2801:  │ │                      │   │   fn=<function _ at 0x7f034b196ca0>,      │ │  
    2802:  │ │                      │   │   module_name='test_agent_routes',        │ │  
    2803:  │ │                      │   │   id='5af32de549da4b6c8484592ceffa8b9a',  │ │  
    2804:  │ │                      │   │   marker=None,                            │ │  
    2805:  │ │                      │   │   description='route: unauthorized should │ │  
    2806:  │ │                      fail',                                          │ │  
    ...
    
    2831:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    2832:  │                                                                          │  
    2833:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    2834:  │ ges/ward/testing.py:637 in _resolve_single_arg                           │  
    2835:  │                                                                          │  
    2836:  │   634 │   │   │   else:                                                  │  
    2837:  │   635 │   │   │   │   fixture.resolved_val = arg(**args_to_inject)       │  
    2838:  │   636 │   │   except (Exception, SystemExit) as e:                       │  
    2839:  │ ❱ 637 │   │   │   raise FixtureError(f"Unable to resolve fixture '{fixtu │  
    ...
    
    2952:  │ │              name = '_dsn'                                           │ │  
    2953:  │ │              self = TestArgumentResolver(                            │ │  
    2954:  │ │                     │   test=Test(                                   │ │  
    2955:  │ │                     │   │   fn=<function _ at 0x7f034b196ca0>,       │ │  
    2956:  │ │                     │   │   module_name='test_agent_routes',         │ │  
    2957:  │ │                     │   │   id='5af32de549da4b6c8484592ceffa8b9a',   │ │  
    2958:  │ │                     │   │   marker=None,                             │ │  
    2959:  │ │                     │   │   description='route: unauthorized should  │ │  
    2960:  │ │                     fail',                                           │ │  
    ...
    
    2979:  │ │                     │   │   timer=<ward._testing._Timer object at    │ │  
    2980:  │ │                     0x7f0349fd2f30>,                                 │ │  
    2981:  │ │                     │   │   tags=[]                                  │ │  
    2982:  │ │                     │   ),                                           │ │  
    2983:  │ │                     │   iteration=0                                  │ │  
    2984:  │ │                     )                                                │ │  
    2985:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    2986:  ╰──────────────────────────────────────────────────────────────────────────╯  
    2987:  FixtureError: Unable to resolve fixture 'client'                              
    2988:  ───────────────────────────── route: create agent ──────────────────────────────
    2989:  Failed at tests/test_agent_routes.py                                          
    ...
    
    3236:  │ │ self = <anyio._backends._asyncio.BlockingPortal object at            │ │  
    3237:  │ │        0x7f0349f97680>                                               │ │  
    3238:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    3239:  │                                                                          │  
    3240:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    3241:  │ python3.12/concurrent/futures/_base.py:456 in result                     │  
    3242:  │                                                                          │  
    3243:  │   453 │   │   │   │   if self._state in [CANCELLED, CANCELLED_AND_NOTIFI │  
    3244:  │   454 │   │   │   │   │   raise CancelledError()                         │  
    3245:  │   455 │   │   │   │   elif self._state == FINISHED:                      │  
    3246:  │ ❱ 456 │   │   │   │   │   return self.__get_result()                     │  
    3247:  │   457 │   │   │   │   else:                                              │  
    3248:  │   458 │   │   │   │   │   raise TimeoutError()                           │  
    ...
    
    3280:  │   221 │   │   except self._cancelled_exc_class:                          │  
    3281:  │                                                                          │  
    3282:  │ ╭─────────────────────────────── locals ───────────────────────────────╮ │  
    3283:  │ │                args = ()                                             │ │  
    3284:  │ │                func = <bound method TestClient.wait_startup of       │ │  
    3285:  │ │                       <starlette.testclient.TestClient object at     │ │  
    3286:  │ │                       0x7f0349f97aa0>>                               │ │  
    3287:  │ │              future = <Future at 0x7f0349f94e60 state=finished       │ │  
    3288:  │ │                       raised ValueError>                             │ │  
    ...
    
    3292:  │ │               scope = None                                           │ │  
    3293:  │ │                self = <anyio._backends._asyncio.BlockingPortal       │ │  
    3294:  │ │                       object at 0x7f0349f97680>                      │ │  
    3295:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    3296:  │                                                                          │  
    3297:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    3298:  │ ges/starlette/testclient.py:774 in wait_startup                          │  
    3299:  │                                                                          │  
    3300:  │   771 │   │   │   "lifespan.startup.failed",                             │  
    3301:  │   772 │   │   )                                                          │  
    3302:  │   773 │   │   if message["type"] == "lifespan.startup.failed":           │  
    3303:  │ ❱ 774 │   │   │   await receive()                                        │  
    3304:  │   775 │                                                                  │  
    3305:  │   776 │   async def wait_shutdown(self) -> None:                         │  
    3306:  │   777 │   │   async def receive() -> typing.Any:                         │  
    3307:  │                                                                          │  
    3308:  │ ╭─────────────────────────────── locals ───────────────────────────────╮ │  
    3309:  │ │ message = {                                                          │ │  
    3310:  │ │           │   'type': 'lifespan.startup.failed',                     │ │  
    ...
    
    3329:  │ │ message = None                                                       │ │  
    3330:  │ │    self = <starlette.testclient.TestClient object at 0x7f0349f97aa0> │ │  
    3331:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    3332:  │                                                                          │  
    3333:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    3334:  │ python3.12/concurrent/futures/_base.py:449 in result                     │  
    3335:  │                                                                          │  
    3336:  │   446 │   │   │   │   if self._state in [CANCELLED, CANCELLED_AND_NOTIFI │  
    3337:  │   447 │   │   │   │   │   raise CancelledError()                         │  
    ...
    
    3373:  │   221 │   │   except self._cancelled_exc_class:                          │  
    3374:  │                                                                          │  
    3375:  │ ╭─────────────────────────────── locals ───────────────────────────────╮ │  
    3376:  │ │                args = ()                                             │ │  
    3377:  │ │                func = <bound method TestClient.lifespan of           │ │  
    3378:  │ │                       <starlette.testclient.TestClient object at     │ │  
    3379:  │ │                       0x7f0349f97aa0>>                               │ │  
    3380:  │ │              future = <Future at 0x7f0349f95190 state=finished       │ │  
    3381:  │ │                       raised ValueError>                             │ │  
    ...
    
    3488:  │ │           waiting_senders=OrderedDict()), _closed=False),            │ │  
    3489:  │ │           receive_stream=MemoryObjectReceiveStream(_state=MemoryObj… │ │  
    3490:  │ │           buffer=deque([]), open_send_channels=1,                    │ │  
    3491:  │ │           open_receive_channels=1, waiting_receivers=OrderedDict(),  │ │  
    3492:  │ │           waiting_senders=OrderedDict()), _closed=False))>           │ │  
    3493:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    3494:  │                                                                          │  
    3495:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    3496:  │ ges/starlette/middleware/errors.py:152 in __call__                       │  
    ...
    
    3516:  │ │   scope = {                                                          │ │  
    3517:  │ │           │   'type': 'lifespan',                                    │ │  
    3518:  │ │           │   'state': {},                                           │ │  
    3519:  │ │           │   'app': <fastapi.applications.FastAPI object at         │ │  
    3520:  │ │           0x7f034cad9190>,                                           │ │  
    3521:  │ │           │   'router': <fastapi.routing.APIRouter object at         │ │  
    3522:  │ │           0x7f034cb6c620>                                            │ │  
    3523:  │ │           }                                                          │ │  
    3524:  │ │    self = <starlette.middleware.errors.ServerErrorMiddleware object  │ │  
    ...
    
    3746:  │ ges/starlette/routing.py:693 in lifespan                                 │  
    3747:  │                                                                          │  
    3748:  │   690 │   │   app: typing.Any = scope.get("app")                         │  
    3749:  │   691 │   │   await receive()                                            │  
    3750:  │   692 │   │   try:                                                       │  
    3751:  │ ❱ 693 │   │   │   async with self.lifespan_context(app) as maybe_state:  │  
    3752:  │   694 │   │   │   │   if maybe_state is not None:                        │  
    3753:  │   695 │   │   │   │   │   if "state" not in scope:                       │  
    3754:  │   696 │   │   │   │   │   │   raise RuntimeError('The server does not su │  
    ...
    
    3790:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    3791:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    3792:  │                                                                          │  
    3793:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    3794:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    3795:  │   209 │   │   try:                                                       │  
    3796:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    3797:  │   211 │   │   except StopAsyncIteration:                                 │  
    3798:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    3824:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    3825:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    3826:  │                                                                          │  
    3827:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    3828:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    3829:  │   209 │   │   try:                                                       │  
    3830:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    3831:  │   211 │   │   except StopAsyncIteration:                                 │  
    3832:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    3858:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    3859:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    3860:  │                                                                          │  
    3861:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    3862:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    3863:  │   209 │   │   try:                                                       │  
    3864:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    3865:  │   211 │   │   except StopAsyncIteration:                                 │  
    3866:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    3892:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    3893:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    3894:  │                                                                          │  
    3895:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    3896:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    3897:  │   209 │   │   try:                                                       │  
    3898:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    3899:  │   211 │   │   except StopAsyncIteration:                                 │  
    3900:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    3926:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    3927:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    3928:  │                                                                          │  
    3929:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    3930:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    3931:  │   209 │   │   try:                                                       │  
    3932:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    3933:  │   211 │   │   except StopAsyncIteration:                                 │  
    3934:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    3960:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    3961:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    3962:  │                                                                          │  
    3963:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    3964:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    3965:  │   209 │   │   try:                                                       │  
    3966:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    3967:  │   211 │   │   except StopAsyncIteration:                                 │  
    3968:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    3994:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    3995:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    3996:  │                                                                          │  
    3997:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    3998:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    3999:  │   209 │   │   try:                                                       │  
    4000:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    4001:  │   211 │   │   except StopAsyncIteration:                                 │  
    4002:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    4028:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    4029:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    4030:  │                                                                          │  
    4031:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    4032:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    4033:  │   209 │   │   try:                                                       │  
    4034:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    4035:  │   211 │   │   except StopAsyncIteration:                                 │  
    4036:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    4062:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    4063:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    4064:  │                                                                          │  
    4065:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    4066:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    4067:  │   209 │   │   try:                                                       │  
    4068:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    4069:  │   211 │   │   except StopAsyncIteration:                                 │  
    4070:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    4096:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    4097:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    4098:  │                                                                          │  
    4099:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    4100:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    4101:  │   209 │   │   try:                                                       │  
    4102:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    4103:  │   211 │   │   except StopAsyncIteration:                                 │  
    4104:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    4130:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    4131:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    4132:  │                                                                          │  
    4133:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    4134:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    4135:  │   209 │   │   try:                                                       │  
    4136:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    4137:  │   211 │   │   except StopAsyncIteration:                                 │  
    4138:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    4201:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    4202:  │                                                                          │  
    4203:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    4204:  │ ges/asyncpg/pool.py:361 in __init__                                      │  
    4205:  │                                                                          │  
    4206:  │    358 │   │   │   │   'min_size is expected to be greater or equal to z │  
    4207:  │    359 │   │                                                             │  
    4208:  │    360 │   │   if min_size > max_size:                                   │  
    4209:  │ ❱  361 │   │   │   raise ValueError('min_size is greater than max_size') │  
    4210:  │    362 │   │                                                             │  
    4211:  │    363 │   │   if max_queries <= 0:                                      │  
    4212:  │    364 │   │   │   raise ValueError('max_queries is expected to be great │  
    ...
    
    4226:  │ │                        max_size = 4                                  │ │  
    4227:  │ │                        min_size = 10                                 │ │  
    4228:  │ │                           reset = None                               │ │  
    4229:  │ │                            self = <asyncpg.pool.Pool object at       │ │  
    4230:  │ │                                   0x7f0349f0dc00>                    │ │  
    4231:  │ │                           setup = None                               │ │  
    4232:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    4233:  ╰──────────────────────────────────────────────────────────────────────────╯  
    4234:  ValueError: min_size is greater than max_size                                 
    ...
    
    4612:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    4613:  │                                                                          │  
    4614:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    4615:  │ ges/ward/testing.py:637 in _resolve_single_arg                           │  
    4616:  │                                                                          │  
    4617:  │   634 │   │   │   else:                                                  │  
    4618:  │   635 │   │   │   │   fixture.resolved_val = arg(**args_to_inject)       │  
    4619:  │   636 │   │   except (Exception, SystemExit) as e:                       │  
    4620:  │ ❱ 637 │   │   │   raise FixtureError(f"Unable to resolve fixture '{fixtu │  
    ...
    
    4759:  │ │                     │   │   timer=<ward._testing._Timer object at    │ │  
    4760:  │ │                     0x7f0349f943e0>,                                 │ │  
    4761:  │ │                     │   │   tags=[]                                  │ │  
    4762:  │ │                     │   ),                                           │ │  
    4763:  │ │                     │   iteration=0                                  │ │  
    4764:  │ │                     )                                                │ │  
    4765:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    4766:  ╰──────────────────────────────────────────────────────────────────────────╯  
    4767:  FixtureError: Unable to resolve fixture 'client'                              
    4768:  ──────────────────── route: create agent with instructions ─────────────────────
    4769:  Failed at tests/test_agent_routes.py                                          
    ...
    
    5017:  │ │ self = <anyio._backends._asyncio.BlockingPortal object at            │ │  
    5018:  │ │        0x7f0349f82f90>                                               │ │  
    5019:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    5020:  │                                                                          │  
    5021:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5022:  │ python3.12/concurrent/futures/_base.py:456 in result                     │  
    5023:  │                                                                          │  
    5024:  │   453 │   │   │   │   if self._state in [CANCELLED, CANCELLED_AND_NOTIFI │  
    5025:  │   454 │   │   │   │   │   raise CancelledError()                         │  
    5026:  │   455 │   │   │   │   elif self._state == FINISHED:                      │  
    5027:  │ ❱ 456 │   │   │   │   │   return self.__get_result()                     │  
    5028:  │   457 │   │   │   │   else:                                              │  
    5029:  │   458 │   │   │   │   │   raise TimeoutError()                           │  
    ...
    
    5061:  │   221 │   │   except self._cancelled_exc_class:                          │  
    5062:  │                                                                          │  
    5063:  │ ╭─────────────────────────────── locals ───────────────────────────────╮ │  
    5064:  │ │                args = ()                                             │ │  
    5065:  │ │                func = <bound method TestClient.wait_startup of       │ │  
    5066:  │ │                       <starlette.testclient.TestClient object at     │ │  
    5067:  │ │                       0x7f0349f94e90>>                               │ │  
    5068:  │ │              future = <Future at 0x7f0349f81790 state=finished       │ │  
    5069:  │ │                       raised ValueError>                             │ │  
    ...
    
    5073:  │ │               scope = None                                           │ │  
    5074:  │ │                self = <anyio._backends._asyncio.BlockingPortal       │ │  
    5075:  │ │                       object at 0x7f0349f82f90>                      │ │  
    5076:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    5077:  │                                                                          │  
    5078:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    5079:  │ ges/starlette/testclient.py:774 in wait_startup                          │  
    5080:  │                                                                          │  
    5081:  │   771 │   │   │   "lifespan.startup.failed",                             │  
    5082:  │   772 │   │   )                                                          │  
    5083:  │   773 │   │   if message["type"] == "lifespan.startup.failed":           │  
    5084:  │ ❱ 774 │   │   │   await receive()                                        │  
    5085:  │   775 │                                                                  │  
    5086:  │   776 │   async def wait_shutdown(self) -> None:                         │  
    5087:  │   777 │   │   async def receive() -> typing.Any:                         │  
    5088:  │                                                                          │  
    5089:  │ ╭─────────────────────────────── locals ───────────────────────────────╮ │  
    5090:  │ │ message = {                                                          │ │  
    5091:  │ │           │   'type': 'lifespan.startup.failed',                     │ │  
    ...
    
    5110:  │ │ message = None                                                       │ │  
    5111:  │ │    self = <starlette.testclient.TestClient object at 0x7f0349f94e90> │ │  
    5112:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    5113:  │                                                                          │  
    5114:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5115:  │ python3.12/concurrent/futures/_base.py:449 in result                     │  
    5116:  │                                                                          │  
    5117:  │   446 │   │   │   │   if self._state in [CANCELLED, CANCELLED_AND_NOTIFI │  
    5118:  │   447 │   │   │   │   │   raise CancelledError()                         │  
    ...
    
    5154:  │   221 │   │   except self._cancelled_exc_class:                          │  
    5155:  │                                                                          │  
    5156:  │ ╭─────────────────────────────── locals ───────────────────────────────╮ │  
    5157:  │ │                args = ()                                             │ │  
    5158:  │ │                func = <bound method TestClient.lifespan of           │ │  
    5159:  │ │                       <starlette.testclient.TestClient object at     │ │  
    5160:  │ │                       0x7f0349f94e90>>                               │ │  
    5161:  │ │              future = <Future at 0x7f0349f82ff0 state=finished       │ │  
    5162:  │ │                       raised ValueError>                             │ │  
    ...
    
    5269:  │ │           waiting_senders=OrderedDict()), _closed=False),            │ │  
    5270:  │ │           receive_stream=MemoryObjectReceiveStream(_state=MemoryObj… │ │  
    5271:  │ │           buffer=deque([]), open_send_channels=1,                    │ │  
    5272:  │ │           open_receive_channels=1, waiting_receivers=OrderedDict(),  │ │  
    5273:  │ │           waiting_senders=OrderedDict()), _closed=False))>           │ │  
    5274:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    5275:  │                                                                          │  
    5276:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    5277:  │ ges/starlette/middleware/errors.py:152 in __call__                       │  
    ...
    
    5297:  │ │   scope = {                                                          │ │  
    5298:  │ │           │   'type': 'lifespan',                                    │ │  
    5299:  │ │           │   'state': {},                                           │ │  
    5300:  │ │           │   'app': <fastapi.applications.FastAPI object at         │ │  
    5301:  │ │           0x7f034cad9190>,                                           │ │  
    5302:  │ │           │   'router': <fastapi.routing.APIRouter object at         │ │  
    5303:  │ │           0x7f034cb6c620>                                            │ │  
    5304:  │ │           }                                                          │ │  
    5305:  │ │    self = <starlette.middleware.errors.ServerErrorMiddleware object  │ │  
    ...
    
    5527:  │ ges/starlette/routing.py:693 in lifespan                                 │  
    5528:  │                                                                          │  
    5529:  │   690 │   │   app: typing.Any = scope.get("app")                         │  
    5530:  │   691 │   │   await receive()                                            │  
    5531:  │   692 │   │   try:                                                       │  
    5532:  │ ❱ 693 │   │   │   async with self.lifespan_context(app) as maybe_state:  │  
    5533:  │   694 │   │   │   │   if maybe_state is not None:                        │  
    5534:  │   695 │   │   │   │   │   if "state" not in scope:                       │  
    5535:  │   696 │   │   │   │   │   │   raise RuntimeError('The server does not su │  
    ...
    
    5571:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5572:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5573:  │                                                                          │  
    5574:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5575:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5576:  │   209 │   │   try:                                                       │  
    5577:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5578:  │   211 │   │   except StopAsyncIteration:                                 │  
    5579:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5605:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5606:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5607:  │                                                                          │  
    5608:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5609:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5610:  │   209 │   │   try:                                                       │  
    5611:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5612:  │   211 │   │   except StopAsyncIteration:                                 │  
    5613:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5639:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5640:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5641:  │                                                                          │  
    5642:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5643:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5644:  │   209 │   │   try:                                                       │  
    5645:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5646:  │   211 │   │   except StopAsyncIteration:                                 │  
    5647:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5673:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5674:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5675:  │                                                                          │  
    5676:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5677:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5678:  │   209 │   │   try:                                                       │  
    5679:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5680:  │   211 │   │   except StopAsyncIteration:                                 │  
    5681:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5707:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5708:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5709:  │                                                                          │  
    5710:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5711:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5712:  │   209 │   │   try:                                                       │  
    5713:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5714:  │   211 │   │   except StopAsyncIteration:                                 │  
    5715:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5741:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5742:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5743:  │                                                                          │  
    5744:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5745:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5746:  │   209 │   │   try:                                                       │  
    5747:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5748:  │   211 │   │   except StopAsyncIteration:                                 │  
    5749:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5775:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5776:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5777:  │                                                                          │  
    5778:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5779:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5780:  │   209 │   │   try:                                                       │  
    5781:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5782:  │   211 │   │   except StopAsyncIteration:                                 │  
    5783:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5809:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5810:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5811:  │                                                                          │  
    5812:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5813:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5814:  │   209 │   │   try:                                                       │  
    5815:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5816:  │   211 │   │   except StopAsyncIteration:                                 │  
    5817:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5843:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5844:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5845:  │                                                                          │  
    5846:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5847:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5848:  │   209 │   │   try:                                                       │  
    5849:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5850:  │   211 │   │   except StopAsyncIteration:                                 │  
    5851:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5877:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5878:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5879:  │                                                                          │  
    5880:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5881:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5882:  │   209 │   │   try:                                                       │  
    5883:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5884:  │   211 │   │   except StopAsyncIteration:                                 │  
    5885:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5911:  │ /home/runner/.local/share/uv/python/cpython-3.12.8-linux-x86_64-gnu/lib/ │  
    5912:  │ python3.12/contextlib.py:210 in __aenter__                               │  
    5913:  │                                                                          │  
    5914:  │   207 │   │   # they are only needed for recreation, which is not possib │  
    5915:  │   208 │   │   del self.args, self.kwds, self.func                        │  
    5916:  │   209 │   │   try:                                                       │  
    5917:  │ ❱ 210 │   │   │   return await anext(self.gen)                           │  
    5918:  │   211 │   │   except StopAsyncIteration:                                 │  
    5919:  │   212 │   │   │   raise RuntimeError("generator didn't yield") from None │  
    ...
    
    5982:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    5983:  │                                                                          │  
    5984:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    5985:  │ ges/asyncpg/pool.py:361 in __init__                                      │  
    5986:  │                                                                          │  
    5987:  │    358 │   │   │   │   'min_size is expected to be greater or equal to z │  
    5988:  │    359 │   │                                                             │  
    5989:  │    360 │   │   if min_size > max_size:                                   │  
    5990:  │ ❱  361 │   │   │   raise ValueError('min_size is greater than max_size') │  
    5991:  │    362 │   │                                                             │  
    5992:  │    363 │   │   if max_queries <= 0:                                      │  
    5993:  │    364 │   │   │   raise ValueError('max_queries is expected to be great │  
    ...
    
    6007:  │ │                        max_size = 4                                  │ │  
    6008:  │ │                        min_size = 10                                 │ │  
    6009:  │ │                           reset = None                               │ │  
    6010:  │ │                            self = <asyncpg.pool.Pool object at       │ │  
    6011:  │ │                                   0x7f0349b54100>                    │ │  
    6012:  │ │                           setup = None                               │ │  
    6013:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    6014:  ╰──────────────────────────────────────────────────────────────────────────╯  
    6015:  ValueError: min_size is greater than max_size                                 
    ...
    
    6395:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    6396:  │                                                                          │  
    6397:  │ /home/runner/work/julep/julep/agents-api/.venv/lib/python3.12/site-packa │  
    6398:  │ ges/ward/testing.py:637 in _resolve_single_arg                           │  
    6399:  │                                                                          │  
    6400:  │   634 │   │   │   else:                                                  │  
    6401:  │   635 │   │   │   │   fixture.resolved_val = arg(**args_to_inject)       │  
    6402:  │   636 │   │   except (Exception, SystemExit) as e:                       │  
    6403:  │ ❱ 637 │   │   │   raise FixtureError(f"Unable to resolve fixture '{fixtu │  
    ...
    
    6543:  │ │                     │   │   timer=<ward._testing._Timer object at    │ │  
    6544:  │ │                     0x7f0349f94d40>,                                 │ │  
    6545:  │ │                     │   │   tags=[]                                  │ │  
    6546:  │ │                     │   ),                                           │ │  
    6547:  │ │                     │   iteration=0                                  │ │  
    6548:  │ │                     )                                                │ │  
    6549:  │ ╰──────────────────────────────────────────────────────────────────────╯ │  
    6550:  ╰──────────────────────────────────────────────────────────────────────────╯  
    6551:  FixtureError: Unable to resolve fixture 'client'                              
    6552:  ────────────────────────────────────────────────────────────────────────────────
    6553:  ╭──────────── Results ─────────────╮
    6554:  │  12  Tests Encountered           │
    6555:  │   9  Passes             (75.0%)  │
    6556:  │   3  Failures           (25.0%)  │
    6557:  ╰──────────────────────────────────╯
    6558:  ─────────────────────────── FAILED in 52.09 seconds ────────────────────────────
    6559:  ##[error]Process completed with exit code 1.
    

    ✨ CI feedback usage guide:

    The CI feedback tool (/checks) automatically triggers when a PR has a failed check.
    The tool analyzes the failed checks and provides several feedbacks:

    • Failed stage
    • Failed test name
    • Failure summary
    • Relevant error logs

    In addition to being automatically triggered, the tool can also be invoked manually by commenting on a PR:

    /checks "https://github.com/{repo_name}/actions/runs/{run_number}/job/{job_number}"
    

    where {repo_name} is the name of the repository, {run_number} is the run number of the failed check, and {job_number} is the job number of ...

    @whiterabbit1983 whiterabbit1983 marked this pull request as ready for review January 14, 2025 13:34
    @whiterabbit1983 whiterabbit1983 merged commit 633164b into dev Jan 14, 2025
    12 of 14 checks passed
    Copy link
    Contributor

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
    🧪 No relevant tests
    🔒 Security concerns

    SQL Injection:
    While using parameterized queries which is good practice, the dynamic JSON/JSONB operations (metadata || $5) should be validated to ensure proper sanitization of the input data before merging with existing metadata.

    ⚡ Recommended focus areas for review

    SQL Parameter Order

    The SQL query references parameters $8 and $9 but there's no validation that these parameters are properly ordered in the query parameters list. Verify parameter ordering matches the SQL references.

    		WHEN $8::text[] IS NOT NULL THEN $8
    		ELSE instructions
    	END,
        canonical_name = CASE
            WHEN $9::citext IS NOT NULL THEN $9
            ELSE canonical_name

    @whiterabbit1983 whiterabbit1983 deleted the x/patch-agent-query branch January 14, 2025 13:35
    Copy link
    Contributor

    @ellipsis-dev ellipsis-dev bot left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    👍 Looks good to me! Reviewed everything up to 56fcfbf in 17 seconds

    More details
    • Looked at 28 lines of code in 1 files
    • Skipped 0 files when reviewing.
    • Skipped posting 1 drafted comments based on config settings.
    1. agents-api/agents_api/queries/agents/patch_agent.py:80
    • Draft comment:
      Ensure that all fields in params are correctly handled for None values. For example, data.name, data.about, data.metadata, data.model, data.instructions, and data.canonical_name should be checked for None before being added to params. This ensures that the SQL query behaves as expected when optional fields are not provided.
    • Reason this comment was not posted:
      Comment did not seem useful.

    Workflow ID: wflow_2mFRcopXB2BkEqVp


    You can customize Ellipsis with 👍 / 👎 feedback, review rules, user-specific overrides, quiet mode, and more.

    Copy link
    Contributor

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Score
    Security
    Add array validation and sanitization to prevent SQL injection and ensure data integrity

    Add input validation for instructions array to prevent SQL injection and ensure data
    integrity. The current implementation directly passes the array to the query without
    sanitization.

    agents-api/agents_api/queries/agents/patch_agent.py [39-42]

     instructions = CASE
    -    WHEN $8::text[] IS NOT NULL THEN $8
    +    WHEN $8::text[] IS NOT NULL AND array_length($8, 1) > 0 THEN array(SELECT trim(i) FROM unnest($8) i WHERE length(trim(i)) > 0)
         ELSE instructions
     END,
    • Apply this suggestion
    Suggestion importance[1-10]: 8

    Why: The suggestion addresses a critical security concern by adding proper validation and sanitization of array inputs, which helps prevent SQL injection and ensures data quality by removing empty strings and whitespace.

    8
    Possible issue
    Add input length validation to prevent data truncation and ensure database constraints are met

    Add length validation for canonical_name to ensure it meets database constraints and
    prevent potential truncation issues.

    agents-api/agents_api/queries/agents/patch_agent.py [43-46]

     canonical_name = CASE
    -    WHEN $9::citext IS NOT NULL THEN $9
    +    WHEN $9::citext IS NOT NULL AND length($9) <= 255 THEN $9
         ELSE canonical_name
     END
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    Why: The suggestion adds important data validation to prevent potential database errors and data truncation issues by enforcing a maximum length constraint on the canonical_name field.

    7

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    1 participant