stores 0.1.8.dev1__tar.gz → 0.1.8.dev2__tar.gz
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.
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/PKG-INFO +1 -1
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/pyproject.toml +1 -1
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/indexes/venv_utils.py +140 -116
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_indexes/test_venv_utils.py +1 -1
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/uv.lock +1 -1
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/.gitignore +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/.python-version +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/LICENSE +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/README.md +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/README.md +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/anthropic_api.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/google_gemini_auto_call.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/google_gemini_manual_call.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/langchain_w_tool_calling.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/langgraph_agent.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/litellm_w_tool_calling.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/llamaindex_agent.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/openai_agent.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/openai_chat_completions.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/examples/quickstarts/openai_responses.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/local_generator.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/run_browser_use.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/run_remote_tool.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/run_sandbox.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/__init__.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/constants.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/format.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/indexes/__init__.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/indexes/base_index.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/indexes/index.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/indexes/local_index.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/indexes/remote_index.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/parse.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/stores/utils.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/README.md +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index/hello/__init__.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index/tools.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index/tools.toml +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index_custom_class/foo.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index_custom_class/tools.toml +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index_function_error/foo.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index_function_error/tools.toml +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index_w_deps/mock_index/__init__.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index_w_deps/pyproject.toml +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index_w_deps/requirements.txt +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/mock_index_w_deps/tools.toml +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_format/conftest.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_format/test_format.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_indexes/conftest.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_indexes/test_base_index.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_indexes/test_index.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_indexes/test_local_index.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_indexes/test_remote_index.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_parse/conftest.py +0 -0
- {stores-0.1.8.dev1 → stores-0.1.8.dev2}/tests/test_parse/test_parse.py +0 -0
@@ -327,8 +327,7 @@ def parse_tool_signature(
|
|
327
327
|
if signature_dict.get("isasyncgenfunction"):
|
328
328
|
|
329
329
|
async def func_handler(*args, **kwargs):
|
330
|
-
|
331
|
-
async for value in run_remote_tool(
|
330
|
+
async for value in run_remote_tool_async(
|
332
331
|
tool_id=signature_dict["tool_id"],
|
333
332
|
index_folder=index_folder,
|
334
333
|
args=args,
|
@@ -338,6 +337,7 @@ def parse_tool_signature(
|
|
338
337
|
stream=True,
|
339
338
|
):
|
340
339
|
yield value
|
340
|
+
|
341
341
|
elif signature_dict.get("isgeneratorfunction"):
|
342
342
|
|
343
343
|
def func_handler(*args, **kwargs):
|
@@ -347,7 +347,7 @@ def parse_tool_signature(
|
|
347
347
|
def run():
|
348
348
|
async def runner():
|
349
349
|
try:
|
350
|
-
async for item in
|
350
|
+
async for item in run_remote_tool_async(
|
351
351
|
tool_id=signature_dict["tool_id"],
|
352
352
|
index_folder=index_folder,
|
353
353
|
args=args,
|
@@ -381,26 +381,63 @@ def parse_tool_signature(
|
|
381
381
|
elif signature_dict.get("iscoroutinefunction"):
|
382
382
|
|
383
383
|
async def func_handler(*args, **kwargs):
|
384
|
-
|
385
|
-
|
384
|
+
result = []
|
385
|
+
async for item in run_remote_tool_async(
|
386
386
|
tool_id=signature_dict["tool_id"],
|
387
387
|
index_folder=index_folder,
|
388
388
|
args=args,
|
389
389
|
kwargs=kwargs,
|
390
390
|
venv=venv,
|
391
391
|
env_var=env_var,
|
392
|
-
|
392
|
+
stream=True,
|
393
|
+
):
|
394
|
+
result.append(item)
|
395
|
+
return result[-1] if result else None
|
393
396
|
else:
|
394
397
|
|
395
|
-
def
|
396
|
-
|
398
|
+
async def func_handler_async_fallback(*args, **kwargs):
|
399
|
+
result = []
|
400
|
+
async for item in run_remote_tool_async(
|
397
401
|
tool_id=signature_dict["tool_id"],
|
398
402
|
index_folder=index_folder,
|
399
403
|
args=args,
|
400
404
|
kwargs=kwargs,
|
401
405
|
venv=venv,
|
402
406
|
env_var=env_var,
|
403
|
-
|
407
|
+
stream=True,
|
408
|
+
):
|
409
|
+
result.append(item)
|
410
|
+
return result[-1] if result else None
|
411
|
+
|
412
|
+
def func_handler(*args, **kwargs):
|
413
|
+
coro = func_handler_async_fallback(*args, **kwargs)
|
414
|
+
try:
|
415
|
+
# Check if we're in an async context
|
416
|
+
asyncio.get_running_loop()
|
417
|
+
in_async = True
|
418
|
+
except RuntimeError:
|
419
|
+
in_async = False
|
420
|
+
|
421
|
+
if not in_async:
|
422
|
+
# Safe to run directly
|
423
|
+
return asyncio.run(coro)
|
424
|
+
|
425
|
+
q = queue.Queue()
|
426
|
+
|
427
|
+
def runner():
|
428
|
+
try:
|
429
|
+
result = asyncio.run(coro)
|
430
|
+
q.put(result)
|
431
|
+
except Exception as e:
|
432
|
+
q.put(e)
|
433
|
+
|
434
|
+
t = threading.Thread(target=runner)
|
435
|
+
t.start()
|
436
|
+
result = q.get()
|
437
|
+
t.join()
|
438
|
+
if isinstance(result, Exception):
|
439
|
+
raise result
|
440
|
+
return result
|
404
441
|
|
405
442
|
# Reconstruct signature from list of args
|
406
443
|
params = []
|
@@ -426,92 +463,7 @@ def parse_tool_signature(
|
|
426
463
|
return func
|
427
464
|
|
428
465
|
|
429
|
-
|
430
|
-
def run_remote_tool(
|
431
|
-
tool_id: str,
|
432
|
-
index_folder: os.PathLike,
|
433
|
-
args: list | None = None,
|
434
|
-
kwargs: dict | None = None,
|
435
|
-
venv: str = VENV_NAME,
|
436
|
-
env_var: dict | None = None,
|
437
|
-
stream: bool = False,
|
438
|
-
):
|
439
|
-
args = args or []
|
440
|
-
kwargs = kwargs or {}
|
441
|
-
env_var = env_var or {}
|
442
|
-
|
443
|
-
module_name = ".".join(tool_id.split(".")[:-1])
|
444
|
-
tool_name = tool_id.split(".")[-1]
|
445
|
-
payload = json.dumps(
|
446
|
-
{
|
447
|
-
"args": args,
|
448
|
-
"kwargs": kwargs,
|
449
|
-
}
|
450
|
-
).encode("utf-8")
|
451
|
-
|
452
|
-
# We use sockets to pass function output
|
453
|
-
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
454
|
-
listener.bind(("localhost", 0))
|
455
|
-
listener.listen(1)
|
456
|
-
_, port = listener.getsockname()
|
457
|
-
|
458
|
-
result_data = {}
|
459
|
-
|
460
|
-
def handle_connection_sync():
|
461
|
-
conn, _ = listener.accept()
|
462
|
-
with conn:
|
463
|
-
buffer = ""
|
464
|
-
while True:
|
465
|
-
chunk = conn.recv(4096).decode("utf-8")
|
466
|
-
if not chunk:
|
467
|
-
break
|
468
|
-
buffer += chunk
|
469
|
-
while "\n" in buffer:
|
470
|
-
line, buffer = buffer.split("\n", 1)
|
471
|
-
if not line.strip():
|
472
|
-
continue
|
473
|
-
msg = json.loads(line)
|
474
|
-
if msg.get("ok") and "stream" in msg:
|
475
|
-
result_data.setdefault("stream", []).append(msg["stream"])
|
476
|
-
elif msg.get("ok") and "result" in msg:
|
477
|
-
result_data["result"] = msg["result"]
|
478
|
-
elif "error" in msg:
|
479
|
-
result_data["error"] = msg["error"]
|
480
|
-
elif msg.get("done"):
|
481
|
-
return
|
482
|
-
|
483
|
-
async def handle_connection_async():
|
484
|
-
loop = asyncio.get_running_loop()
|
485
|
-
conn, _ = await loop.sock_accept(listener)
|
486
|
-
conn.setblocking(False)
|
487
|
-
buffer = ""
|
488
|
-
try:
|
489
|
-
while True:
|
490
|
-
chunk = await loop.sock_recv(conn, 4096)
|
491
|
-
if not chunk:
|
492
|
-
break
|
493
|
-
buffer += chunk.decode("utf-8")
|
494
|
-
while "\n" in buffer:
|
495
|
-
line, buffer = buffer.split("\n", 1)
|
496
|
-
if not line.strip():
|
497
|
-
continue
|
498
|
-
msg = json.loads(line)
|
499
|
-
if msg.get("ok") and "stream" in msg:
|
500
|
-
yield msg["stream"]
|
501
|
-
elif msg.get("ok") and "result" in msg:
|
502
|
-
yield msg["result"]
|
503
|
-
elif "error" in msg:
|
504
|
-
raise RuntimeError(f"Subprocess error:\n{msg['error']}")
|
505
|
-
elif msg.get("done"):
|
506
|
-
return
|
507
|
-
finally:
|
508
|
-
conn.close()
|
509
|
-
|
510
|
-
if not stream:
|
511
|
-
thread = threading.Thread(target=lambda: handle_connection_sync())
|
512
|
-
thread.start()
|
513
|
-
|
514
|
-
runner = f"""
|
466
|
+
tool_runner = """
|
515
467
|
import asyncio, inspect, json, socket, sys, traceback
|
516
468
|
sys.path.insert(0, "{index_folder}")
|
517
469
|
|
@@ -560,26 +512,98 @@ finally:
|
|
560
512
|
pass
|
561
513
|
"""
|
562
514
|
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
515
|
+
|
516
|
+
# TODO: Sanitize tool_id, args, and kwargs
|
517
|
+
async def run_remote_tool_async(
|
518
|
+
tool_id: str,
|
519
|
+
index_folder: os.PathLike,
|
520
|
+
args: list | None = None,
|
521
|
+
kwargs: dict | None = None,
|
522
|
+
venv: str = VENV_NAME,
|
523
|
+
env_var: dict | None = None,
|
524
|
+
stream: bool = True,
|
525
|
+
):
|
526
|
+
args = args or []
|
527
|
+
kwargs = kwargs or {}
|
528
|
+
env_var = env_var or {}
|
529
|
+
|
530
|
+
module_name = ".".join(tool_id.split(".")[:-1])
|
531
|
+
tool_name = tool_id.split(".")[-1]
|
532
|
+
payload = json.dumps({"args": args, "kwargs": kwargs}).encode("utf-8")
|
533
|
+
|
534
|
+
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
535
|
+
listener.bind(("localhost", 0))
|
536
|
+
listener.listen(1)
|
537
|
+
listener.setblocking(False)
|
538
|
+
_, port = listener.getsockname()
|
539
|
+
|
540
|
+
loop = asyncio.get_running_loop()
|
541
|
+
conn_task = loop.create_task(loop.sock_accept(listener))
|
542
|
+
|
543
|
+
runner = tool_runner.format(
|
544
|
+
index_folder=index_folder,
|
545
|
+
port=port,
|
546
|
+
module_name=module_name,
|
547
|
+
tool_name=tool_name,
|
548
|
+
)
|
549
|
+
|
550
|
+
proc = await asyncio.create_subprocess_exec(
|
551
|
+
get_python_command(Path(index_folder) / venv),
|
552
|
+
"-c",
|
553
|
+
runner,
|
554
|
+
stdin=asyncio.subprocess.PIPE,
|
555
|
+
stdout=asyncio.subprocess.DEVNULL,
|
556
|
+
stderr=asyncio.subprocess.PIPE,
|
568
557
|
env=env_var or None,
|
569
558
|
)
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
559
|
+
|
560
|
+
try:
|
561
|
+
proc.stdin.write(payload)
|
562
|
+
await proc.stdin.drain()
|
563
|
+
proc.stdin.close()
|
564
|
+
|
565
|
+
conn, _ = await conn_task
|
566
|
+
conn.setblocking(False)
|
567
|
+
|
568
|
+
buffer = ""
|
569
|
+
result = None
|
570
|
+
while True:
|
571
|
+
chunk = await loop.sock_recv(conn, 4096)
|
572
|
+
if not chunk:
|
573
|
+
break
|
574
|
+
buffer += chunk.decode("utf-8")
|
575
|
+
while "\n" in buffer:
|
576
|
+
line, buffer = buffer.split("\n", 1)
|
577
|
+
if not line.strip():
|
578
|
+
continue
|
579
|
+
msg = json.loads(line)
|
580
|
+
|
581
|
+
if msg.get("ok") and "stream" in msg:
|
582
|
+
if stream:
|
583
|
+
yield msg["stream"]
|
584
|
+
else:
|
585
|
+
result = msg["stream"]
|
586
|
+
elif msg.get("ok") and "result" in msg:
|
587
|
+
result = msg["result"]
|
588
|
+
elif "error" in msg:
|
589
|
+
raise RuntimeError(f"Subprocess error:\n{msg['error']}")
|
590
|
+
elif "done" in msg and result is not None:
|
591
|
+
yield result
|
592
|
+
return
|
593
|
+
|
594
|
+
except asyncio.CancelledError:
|
595
|
+
proc.kill()
|
596
|
+
await proc.wait()
|
597
|
+
raise
|
598
|
+
finally:
|
599
|
+
try:
|
600
|
+
conn.close()
|
601
|
+
except Exception:
|
602
|
+
pass
|
603
|
+
try:
|
604
|
+
listener.close()
|
605
|
+
except Exception:
|
606
|
+
pass
|
607
|
+
if proc.returncode is None:
|
608
|
+
proc.kill()
|
609
|
+
await proc.wait()
|
@@ -134,7 +134,7 @@ def test_index_with_invalid_tool(index_folder_custom_class):
|
|
134
134
|
|
135
135
|
def test_index_with_tool_error(index_folder_function_error):
|
136
136
|
tools = venv_utils.init_venv_tools(index_folder_function_error)
|
137
|
-
with pytest.raises(RuntimeError, match="
|
137
|
+
with pytest.raises(RuntimeError, match="ZeroDivisionError"):
|
138
138
|
tools[0]()
|
139
139
|
|
140
140
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|