vectorsmith 0.1.0__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.
Files changed (73) hide show
  1. vectorsmith-0.1.0/.gitignore +44 -0
  2. vectorsmith-0.1.0/PKG-INFO +77 -0
  3. vectorsmith-0.1.0/README.md +15 -0
  4. vectorsmith-0.1.0/hatch_build.py +26 -0
  5. vectorsmith-0.1.0/pyproject.toml +65 -0
  6. vectorsmith-0.1.0/vectorsmith/__init__.py +31 -0
  7. vectorsmith-0.1.0/vectorsmith/anthropic.py +42 -0
  8. vectorsmith-0.1.0/vectorsmith/langchain.py +5 -0
  9. vectorsmith-0.1.0/vectorsmith/langchain_tools.py +110 -0
  10. vectorsmith-0.1.0/vectorsmith/langgraph.py +5 -0
  11. vectorsmith-0.1.0/vectorsmith/openai_agents.py +69 -0
  12. vectorsmith-0.1.0/vectorsmith/runtime.py +139 -0
  13. vectorsmith-0.1.0/vectorsmith_cli/__init__.py +1 -0
  14. vectorsmith-0.1.0/vectorsmith_cli/drafts_cmd.py +71 -0
  15. vectorsmith-0.1.0/vectorsmith_cli/http/__init__.py +1 -0
  16. vectorsmith-0.1.0/vectorsmith_cli/http/app.py +136 -0
  17. vectorsmith-0.1.0/vectorsmith_cli/http/builtin_oauth/__init__.py +1 -0
  18. vectorsmith-0.1.0/vectorsmith_cli/http/builtin_oauth/pages.py +16 -0
  19. vectorsmith-0.1.0/vectorsmith_cli/http/builtin_oauth/server.py +123 -0
  20. vectorsmith-0.1.0/vectorsmith_cli/http/builtin_oauth/store.py +198 -0
  21. vectorsmith-0.1.0/vectorsmith_cli/identity.py +4 -0
  22. vectorsmith-0.1.0/vectorsmith_cli/init_cmd.py +100 -0
  23. vectorsmith-0.1.0/vectorsmith_cli/introspect_cmd.py +46 -0
  24. vectorsmith-0.1.0/vectorsmith_cli/main.py +152 -0
  25. vectorsmith-0.1.0/vectorsmith_cli/serve_common.py +274 -0
  26. vectorsmith-0.1.0/vectorsmith_cli/serve_http.py +65 -0
  27. vectorsmith-0.1.0/vectorsmith_cli/serve_stdio.py +219 -0
  28. vectorsmith-0.1.0/vectorsmith_cli/stdio_guard.py +41 -0
  29. vectorsmith-0.1.0/vectorsmith_cli/test_cmd.py +68 -0
  30. vectorsmith-0.1.0/vectorsmith_cli/validate_cmd.py +68 -0
  31. vectorsmith-0.1.0/vectorsmith_core/__init__.py +26 -0
  32. vectorsmith-0.1.0/vectorsmith_core/adapters/__init__.py +1 -0
  33. vectorsmith-0.1.0/vectorsmith_core/adapters/base.py +62 -0
  34. vectorsmith-0.1.0/vectorsmith_core/adapters/capabilities.py +124 -0
  35. vectorsmith-0.1.0/vectorsmith_core/adapters/chroma.py +128 -0
  36. vectorsmith-0.1.0/vectorsmith_core/adapters/milvus.py +129 -0
  37. vectorsmith-0.1.0/vectorsmith_core/adapters/pgvector.py +186 -0
  38. vectorsmith-0.1.0/vectorsmith_core/adapters/pinecone.py +106 -0
  39. vectorsmith-0.1.0/vectorsmith_core/adapters/qdrant.py +270 -0
  40. vectorsmith-0.1.0/vectorsmith_core/adapters/registry.py +1 -0
  41. vectorsmith-0.1.0/vectorsmith_core/adapters/weaviate.py +107 -0
  42. vectorsmith-0.1.0/vectorsmith_core/api.py +220 -0
  43. vectorsmith-0.1.0/vectorsmith_core/compilepkg/__init__.py +1 -0
  44. vectorsmith-0.1.0/vectorsmith_core/compilepkg/builtins.py +157 -0
  45. vectorsmith-0.1.0/vectorsmith_core/compilepkg/compiler.py +148 -0
  46. vectorsmith-0.1.0/vectorsmith_core/compilepkg/drafts.py +113 -0
  47. vectorsmith-0.1.0/vectorsmith_core/compilepkg/validator.py +287 -0
  48. vectorsmith-0.1.0/vectorsmith_core/embed/__init__.py +1 -0
  49. vectorsmith-0.1.0/vectorsmith_core/embed/models.py +7 -0
  50. vectorsmith-0.1.0/vectorsmith_core/embed/provider.py +55 -0
  51. vectorsmith-0.1.0/vectorsmith_core/errors.py +98 -0
  52. vectorsmith-0.1.0/vectorsmith_core/execute/__init__.py +1 -0
  53. vectorsmith-0.1.0/vectorsmith_core/execute/engine.py +236 -0
  54. vectorsmith-0.1.0/vectorsmith_core/execute/expr/__init__.py +6 -0
  55. vectorsmith-0.1.0/vectorsmith_core/execute/expr/eval_polars.py +87 -0
  56. vectorsmith-0.1.0/vectorsmith_core/execute/expr/grammar.lark +44 -0
  57. vectorsmith-0.1.0/vectorsmith_core/execute/expr/parser.py +144 -0
  58. vectorsmith-0.1.0/vectorsmith_core/execute/pipeline.py +143 -0
  59. vectorsmith-0.1.0/vectorsmith_core/execute/single_step.py +146 -0
  60. vectorsmith-0.1.0/vectorsmith_core/introspect/__init__.py +1 -0
  61. vectorsmith-0.1.0/vectorsmith_core/introspect/sampling.py +115 -0
  62. vectorsmith-0.1.0/vectorsmith_core/introspect/schema_export.py +65 -0
  63. vectorsmith-0.1.0/vectorsmith_core/introspect/schema_export_v1.json +11 -0
  64. vectorsmith-0.1.0/vectorsmith_core/introspect/types.py +1 -0
  65. vectorsmith-0.1.0/vectorsmith_core/ir/__init__.py +1 -0
  66. vectorsmith-0.1.0/vectorsmith_core/ir/filter.py +104 -0
  67. vectorsmith-0.1.0/vectorsmith_core/py.typed +0 -0
  68. vectorsmith-0.1.0/vectorsmith_core/tds/__init__.py +1 -0
  69. vectorsmith-0.1.0/vectorsmith_core/tds/loader.py +214 -0
  70. vectorsmith-0.1.0/vectorsmith_core/tds/models.py +262 -0
  71. vectorsmith-0.1.0/vectorsmith_core/tds/schema.py +19 -0
  72. vectorsmith-0.1.0/vectorsmith_core/tds/schema_v1.json +1228 -0
  73. vectorsmith-0.1.0/vectorsmith_core/version.py +4 -0
@@ -0,0 +1,44 @@
1
+ # Virtualenv and Python
2
+ .venv/
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+ *.egg-info/
7
+ .eggs/
8
+ dist/
9
+ build/
10
+ *.so
11
+
12
+ # Tooling
13
+ .mypy_cache/
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ .hypothesis/
17
+ .import_linter_cache/
18
+ .coverage
19
+ coverage.xml
20
+ htmlcov/
21
+ .tox/
22
+ .nox/
23
+
24
+ # Secrets and local env (keep committed *.example)
25
+ .env
26
+ .env.*
27
+ !.env.example
28
+
29
+ # Editors and OS
30
+ .DS_Store
31
+ .idea/
32
+ .vscode/
33
+ *.swp
34
+ .cursor/
35
+
36
+ # Unpublished working notes (design dumps, idea PDF)
37
+ .internal/
38
+
39
+ # Local drafts produced by serve --enable-define
40
+ tools.drafts.yaml
41
+ **/tools.drafts.yaml
42
+
43
+ # MkDocs
44
+ site/
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.5
2
+ Name: vectorsmith
3
+ Version: 0.1.0
4
+ Summary: YAML tools for agents: load_tools in-process, or vectorsmith serve as MCP
5
+ Project-URL: Homepage, https://github.com/kjgpta/vectorsmith
6
+ Project-URL: Documentation, https://kjgpta.github.io/vectorsmith/
7
+ Project-URL: Issues, https://github.com/kjgpta/vectorsmith/issues
8
+ Project-URL: Changelog, https://github.com/kjgpta/vectorsmith/blob/main/CHANGELOG.md
9
+ Author: VectorSmith contributors
10
+ License: Apache-2.0
11
+ Keywords: agents,claude,codex,langchain,langgraph,mcp
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: argon2-cffi
22
+ Requires-Dist: httpx
23
+ Requires-Dist: jsonschema
24
+ Requires-Dist: lark
25
+ Requires-Dist: mcp>=1.2
26
+ Requires-Dist: polars>=1.0
27
+ Requires-Dist: pydantic>=2.7
28
+ Requires-Dist: pyyaml
29
+ Requires-Dist: ruamel-yaml
30
+ Requires-Dist: starlette
31
+ Requires-Dist: typer
32
+ Requires-Dist: uvicorn
33
+ Requires-Dist: watchfiles
34
+ Provides-Extra: anthropic
35
+ Requires-Dist: anthropic>=0.40; extra == 'anthropic'
36
+ Provides-Extra: chroma
37
+ Requires-Dist: chromadb>=0.5; extra == 'chroma'
38
+ Requires-Dist: fastembed>=0.3; extra == 'chroma'
39
+ Provides-Extra: langchain
40
+ Requires-Dist: langchain-core>=0.3; extra == 'langchain'
41
+ Provides-Extra: langgraph
42
+ Requires-Dist: langchain-core>=0.3; extra == 'langgraph'
43
+ Requires-Dist: langgraph>=0.2; extra == 'langgraph'
44
+ Provides-Extra: milvus
45
+ Requires-Dist: fastembed>=0.3; extra == 'milvus'
46
+ Requires-Dist: pymilvus>=2.4; extra == 'milvus'
47
+ Provides-Extra: openai-agents
48
+ Requires-Dist: openai-agents>=0.2; extra == 'openai-agents'
49
+ Provides-Extra: pgvector
50
+ Requires-Dist: fastembed>=0.3; extra == 'pgvector'
51
+ Requires-Dist: psycopg[binary,pool]>=3.2; extra == 'pgvector'
52
+ Provides-Extra: pinecone
53
+ Requires-Dist: fastembed>=0.3; extra == 'pinecone'
54
+ Requires-Dist: pinecone>=5.0; extra == 'pinecone'
55
+ Provides-Extra: qdrant
56
+ Requires-Dist: fastembed>=0.3; extra == 'qdrant'
57
+ Requires-Dist: qdrant-client>=1.9; extra == 'qdrant'
58
+ Provides-Extra: weaviate
59
+ Requires-Dist: fastembed>=0.3; extra == 'weaviate'
60
+ Requires-Dist: weaviate-client>=4.0; extra == 'weaviate'
61
+ Description-Content-Type: text/markdown
62
+
63
+ # vectorsmith
64
+
65
+ Install this package. Write a `tools.yaml`. Use `load_tools` in Python or `vectorsmith serve` as an MCP server.
66
+
67
+ ```bash
68
+ pip install "vectorsmith[qdrant,langchain]"
69
+ ```
70
+
71
+ ```python
72
+ from vectorsmith import load_tools
73
+
74
+ tools = load_tools("tools.yaml")
75
+ ```
76
+
77
+ See the repository `README.md` and `docs/` for integrations and the YAML reference.
@@ -0,0 +1,15 @@
1
+ # vectorsmith
2
+
3
+ Install this package. Write a `tools.yaml`. Use `load_tools` in Python or `vectorsmith serve` as an MCP server.
4
+
5
+ ```bash
6
+ pip install "vectorsmith[qdrant,langchain]"
7
+ ```
8
+
9
+ ```python
10
+ from vectorsmith import load_tools
11
+
12
+ tools = load_tools("tools.yaml")
13
+ ```
14
+
15
+ See the repository `README.md` and `docs/` for integrations and the YAML reference.
@@ -0,0 +1,26 @@
1
+ """Ship ``vectorsmith_core`` inside the ``vectorsmith`` wheel (not a second PyPI project)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
9
+
10
+
11
+ class CustomBuildHook(BuildHookInterface):
12
+ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
13
+ del version
14
+ root = Path(self.root)
15
+ local = root / "vectorsmith_core"
16
+ sibling = root.parent / "core" / "vectorsmith_core"
17
+ if (local / "__init__.py").is_file():
18
+ src = local
19
+ elif (sibling / "__init__.py").is_file():
20
+ src = sibling
21
+ else:
22
+ raise RuntimeError(
23
+ "vectorsmith_core not found at ./vectorsmith_core or ../core/vectorsmith_core"
24
+ )
25
+ build_data.setdefault("force_include", {})
26
+ build_data["force_include"][str(src)] = "vectorsmith_core"
@@ -0,0 +1,65 @@
1
+ [project]
2
+ name = "vectorsmith"
3
+ version = "0.1.0"
4
+ description = "YAML tools for agents: load_tools in-process, or vectorsmith serve as MCP"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = { text = "Apache-2.0" }
8
+ authors = [{ name = "VectorSmith contributors" }]
9
+ keywords = ["mcp", "langchain", "langgraph", "claude", "codex", "agents"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "License :: OSI Approved :: Apache Software License",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3.11",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Programming Language :: Python :: 3.13",
17
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
18
+ "Typing :: Typed",
19
+ ]
20
+ dependencies = [
21
+ "pydantic>=2.7",
22
+ "pyyaml",
23
+ "jsonschema",
24
+ "polars>=1.0",
25
+ "lark",
26
+ "httpx",
27
+ "typer",
28
+ "watchfiles",
29
+ "uvicorn",
30
+ "starlette",
31
+ "mcp>=1.2",
32
+ "argon2-cffi",
33
+ "ruamel.yaml",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/kjgpta/vectorsmith"
38
+ Documentation = "https://kjgpta.github.io/vectorsmith/"
39
+ Issues = "https://github.com/kjgpta/vectorsmith/issues"
40
+ Changelog = "https://github.com/kjgpta/vectorsmith/blob/main/CHANGELOG.md"
41
+
42
+ [project.scripts]
43
+ vectorsmith = "vectorsmith_cli.main:app"
44
+
45
+ [project.optional-dependencies]
46
+ qdrant = ["qdrant-client>=1.9", "fastembed>=0.3"]
47
+ pgvector = ["psycopg[binary,pool]>=3.2", "fastembed>=0.3"]
48
+ chroma = ["chromadb>=0.5", "fastembed>=0.3"]
49
+ pinecone = ["pinecone>=5.0", "fastembed>=0.3"]
50
+ weaviate = ["weaviate-client>=4.0", "fastembed>=0.3"]
51
+ milvus = ["pymilvus>=2.4", "fastembed>=0.3"]
52
+ langchain = ["langchain-core>=0.3"]
53
+ langgraph = ["langchain-core>=0.3", "langgraph>=0.2"]
54
+ openai-agents = ["openai-agents>=0.2"]
55
+ anthropic = ["anthropic>=0.40"]
56
+
57
+ [build-system]
58
+ requires = ["hatchling"]
59
+ build-backend = "hatchling.build"
60
+
61
+ [tool.hatch.build.targets.wheel]
62
+ packages = ["vectorsmith_cli", "vectorsmith"]
63
+
64
+ [tool.hatch.build.hooks.custom]
65
+ path = "hatch_build.py"
@@ -0,0 +1,31 @@
1
+ """VectorSmith application API.
2
+
3
+ In-process (LangChain, LangGraph, OpenAI Agents, Anthropic SDK)::
4
+
5
+ from vectorsmith import load_tools # LangChain / LangGraph
6
+ from vectorsmith import connect # call() or .as_openai_agents() / .as_anthropic()
7
+
8
+ MCP hosts (Claude Desktop, Claude Code, Codex, Cursor)::
9
+
10
+ vectorsmith serve tools.yaml --name invoices
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import TYPE_CHECKING, Any
16
+
17
+ from vectorsmith.runtime import BoundTools, connect
18
+
19
+ if TYPE_CHECKING:
20
+ from vectorsmith.langchain_tools import Toolset
21
+ from vectorsmith.langchain_tools import load_tools as load_tools
22
+
23
+ __all__ = ["BoundTools", "Toolset", "connect", "load_tools"]
24
+
25
+
26
+ def __getattr__(name: str) -> Any:
27
+ if name in {"load_tools", "Toolset"}:
28
+ from vectorsmith.langchain_tools import Toolset, load_tools
29
+
30
+ return {"load_tools": load_tools, "Toolset": Toolset}[name]
31
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,42 @@
1
+ """Anthropic Messages API tools from a tools.yaml.
2
+
3
+ pip install "vectorsmith[qdrant]" anthropic
4
+ from vectorsmith.anthropic import load_tools
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from collections.abc import Mapping
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from vectorsmith.runtime import BoundTools, connect
15
+
16
+
17
+ class AnthropicToolset:
18
+ """``tools`` for ``messages.create``, plus ``execute`` for ``tool_use`` blocks."""
19
+
20
+ def __init__(self, bound: BoundTools) -> None:
21
+ self._bound = bound
22
+ self.tools = bound.as_anthropic()
23
+
24
+ @property
25
+ def names(self) -> list[str]:
26
+ return self._bound.names
27
+
28
+ async def execute(self, name: str, tool_input: Mapping[str, Any] | None = None) -> str:
29
+ result = await self._bound.call(name, tool_input)
30
+ return json.dumps(result)
31
+
32
+ async def aclose(self) -> None:
33
+ await self._bound.aclose()
34
+
35
+
36
+ def load_tools(
37
+ *sources: str | Path | dict[str, Any],
38
+ env: Mapping[str, str] | None = None,
39
+ env_file: str | Path | None = None,
40
+ ) -> AnthropicToolset:
41
+ """Compile tools.yaml into Anthropic tool defs + an in-process dispatcher."""
42
+ return AnthropicToolset(connect(*sources, env=env, env_file=env_file))
@@ -0,0 +1,5 @@
1
+ """LangChain adapter. Same as ``from vectorsmith import load_tools``."""
2
+
3
+ from vectorsmith.langchain_tools import Toolset, load_tools
4
+
5
+ __all__ = ["Toolset", "load_tools"]
@@ -0,0 +1,110 @@
1
+ """LangChain / LangGraph tools from a tools.yaml.
2
+
3
+ Application code: write YAML, then ``from vectorsmith import load_tools``.
4
+ Hosts that cannot import Python (Claude Desktop, Codex, Cursor) use
5
+ ``vectorsmith serve`` instead. Same YAML.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import uuid
12
+ from collections.abc import Mapping, Sequence
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from langchain_core.tools import BaseTool, StructuredTool
17
+ from pydantic import BaseModel, Field, create_model
18
+
19
+ from vectorsmith.runtime import BoundTools, connect
20
+ from vectorsmith_core.api import CallContext
21
+ from vectorsmith_core.execute.engine import Engine
22
+
23
+ _JSON_TO_PY: dict[str, type] = {
24
+ "string": str,
25
+ "integer": int,
26
+ "number": float,
27
+ "boolean": bool,
28
+ }
29
+
30
+
31
+ class Toolset(list[BaseTool]):
32
+ """LangChain tools plus the engines that back them. Call ``aclose`` when done."""
33
+
34
+ def __init__(self, tools: Sequence[BaseTool], engines: Sequence[Engine]) -> None:
35
+ super().__init__(tools)
36
+ self._engines = list(engines)
37
+
38
+ async def aclose(self) -> None:
39
+ for engine in self._engines:
40
+ await engine.aclose()
41
+
42
+
43
+ def _py_type(spec: dict[str, Any]) -> type:
44
+ if spec.get("type") == "array":
45
+ item = _JSON_TO_PY.get((spec.get("items") or {}).get("type", "string"), str)
46
+ return list[item] # type: ignore[valid-type]
47
+ return _JSON_TO_PY.get(str(spec.get("type", "string")), str)
48
+
49
+
50
+ def _args_model(tool_name: str, input_schema: dict[str, Any]) -> type[BaseModel]:
51
+ props = input_schema.get("properties") or {}
52
+ required = set(input_schema.get("required") or [])
53
+ fields: dict[str, Any] = {}
54
+ for key, spec in props.items():
55
+ typ = _py_type(spec if isinstance(spec, dict) else {})
56
+ desc = spec.get("description") if isinstance(spec, dict) else None
57
+ if key in required:
58
+ fields[key] = (typ, Field(..., description=desc))
59
+ else:
60
+ default = spec.get("default") if isinstance(spec, dict) else None
61
+ fields[key] = (typ | None, Field(default, description=desc))
62
+ if not fields:
63
+ return create_model(f"{tool_name}Args")
64
+ return create_model(f"{tool_name}Args", **fields)
65
+
66
+
67
+ def _bind(engine: Engine, name: str, schema: dict[str, Any]) -> StructuredTool:
68
+ async def _arun(**kwargs: Any) -> dict[str, Any]:
69
+ clean = {k: v for k, v in kwargs.items() if v is not None}
70
+ result = await engine.call(
71
+ name, clean, ctx=CallContext(request_id=str(uuid.uuid4()))
72
+ )
73
+ return result.model_dump()
74
+
75
+ def _run(**kwargs: Any) -> dict[str, Any]:
76
+ try:
77
+ asyncio.get_running_loop()
78
+ except RuntimeError:
79
+ return asyncio.run(_arun(**kwargs))
80
+ raise RuntimeError(f"tool {name!r} must be awaited in an async context")
81
+
82
+ return StructuredTool(
83
+ name=name,
84
+ description=schema.get("description") or name,
85
+ args_schema=_args_model(name, schema.get("inputSchema") or {}),
86
+ coroutine=_arun,
87
+ func=_run,
88
+ )
89
+
90
+
91
+ def toolset_from_bound(bound: BoundTools) -> Toolset:
92
+ tools: list[BaseTool] = []
93
+ for engine in bound._engines:
94
+ for schema in engine.project.mcp_tool_schemas():
95
+ tools.append(_bind(engine, str(schema["name"]), schema))
96
+ return Toolset(tools, bound._engines)
97
+
98
+
99
+ def load_tools(
100
+ *sources: str | Path | dict[str, Any],
101
+ env: Mapping[str, str] | None = None,
102
+ env_file: str | Path | None = None,
103
+ ) -> Toolset:
104
+ """Compile one or more tools.yaml files into LangChain / LangGraph tools.
105
+
106
+ >>> from vectorsmith import load_tools
107
+ >>> tools = load_tools("tools.invoices.yaml", "tools.tickets.yaml")
108
+ >>> # pass ``tools`` into create_agent / create_react_agent; await tools.aclose()
109
+ """
110
+ return connect(*sources, env=env, env_file=env_file).as_langchain()
@@ -0,0 +1,5 @@
1
+ """LangGraph adapter. Same LangChain tools (`ToolNode`, `create_react_agent`)."""
2
+
3
+ from vectorsmith.langchain_tools import Toolset, load_tools
4
+
5
+ __all__ = ["Toolset", "load_tools"]
@@ -0,0 +1,69 @@
1
+ """OpenAI Agents SDK tools from a tools.yaml.
2
+
3
+ pip install "vectorsmith[qdrant,openai-agents]"
4
+ from vectorsmith.openai_agents import load_tools
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from collections.abc import Mapping, Sequence
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from vectorsmith.runtime import BoundTools, connect
15
+
16
+
17
+ class OpenAIAgentToolset(list[Any]):
18
+ """FunctionTool list plus ``aclose`` for the backing engines."""
19
+
20
+ def __init__(self, tools: Sequence[Any], bound: BoundTools) -> None:
21
+ super().__init__(tools)
22
+ self._bound = bound
23
+
24
+ async def aclose(self) -> None:
25
+ await self._bound.aclose()
26
+
27
+
28
+ def _function_tool(bound: BoundTools, schema: dict[str, Any]) -> Any:
29
+ try:
30
+ from agents import FunctionTool
31
+ except ImportError as exc:
32
+ raise ImportError(
33
+ "OpenAI Agents SDK is required. Install with: "
34
+ 'pip install "vectorsmith[openai-agents]"'
35
+ ) from exc
36
+
37
+ name = str(schema["name"])
38
+ params = schema.get("inputSchema") or {"type": "object", "properties": {}}
39
+
40
+ async def _on_invoke(_ctx: Any, raw: str) -> str:
41
+ try:
42
+ args = json.loads(raw) if raw else {}
43
+ except json.JSONDecodeError:
44
+ args = {}
45
+ if not isinstance(args, dict):
46
+ return json.dumps({"error": "tool arguments must be a JSON object"})
47
+ result = await bound.call(name, args)
48
+ return json.dumps(result)
49
+
50
+ return FunctionTool(
51
+ name=name,
52
+ description=schema.get("description") or name,
53
+ params_json_schema=params,
54
+ on_invoke_tool=_on_invoke,
55
+ strict_json_schema=False,
56
+ )
57
+
58
+
59
+ def toolset_from_bound(bound: BoundTools) -> OpenAIAgentToolset:
60
+ return OpenAIAgentToolset([_function_tool(bound, s) for s in bound.schemas], bound)
61
+
62
+
63
+ def load_tools(
64
+ *sources: str | Path | dict[str, Any],
65
+ env: Mapping[str, str] | None = None,
66
+ env_file: str | Path | None = None,
67
+ ) -> OpenAIAgentToolset:
68
+ """Compile tools.yaml into OpenAI Agents SDK ``FunctionTool``s."""
69
+ return connect(*sources, env=env, env_file=env_file).as_openai_agents()
@@ -0,0 +1,139 @@
1
+ """Compile tools.yaml in-process. Framework adapters wrap this; MCP hosts use ``serve``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import uuid
7
+ from collections.abc import Mapping, Sequence
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from vectorsmith_core.api import CallContext, EnvCredentialResolver, load_project
12
+ from vectorsmith_core.execute.engine import Engine
13
+
14
+
15
+ def _read_env_file(path: Path | None) -> dict[str, str]:
16
+ if path is None or not path.is_file():
17
+ return {}
18
+ out: dict[str, str] = {}
19
+ for line in path.read_text().splitlines():
20
+ line = line.strip()
21
+ if not line or line.startswith("#") or "=" not in line:
22
+ continue
23
+ key, value = line.split("=", 1)
24
+ out[key.strip()] = value.strip()
25
+ return out
26
+
27
+
28
+ def _merge_env(
29
+ env: Mapping[str, str] | None,
30
+ env_file: str | Path | None,
31
+ ) -> dict[str, str]:
32
+ merged = dict(os.environ)
33
+ if env:
34
+ merged.update(env)
35
+ merged.update(_read_env_file(Path(env_file) if env_file else None))
36
+ return merged
37
+
38
+
39
+ def _one_engine(source: str | Path | dict[str, Any], env: Mapping[str, str]) -> Engine:
40
+ project = load_project(source, env=env)
41
+ errors = [i for i in project.issues if i.severity == "error"]
42
+ if errors:
43
+ msgs = "; ".join(f"{i.code} {i.message}" for i in errors)
44
+ raise ValueError(f"tools.yaml failed validation: {msgs}")
45
+ try:
46
+ from vectorsmith_core.embed.provider import FastEmbedProvider
47
+
48
+ embed: FastEmbedProvider | None = FastEmbedProvider()
49
+ except Exception:
50
+ embed = None
51
+ return Engine(
52
+ project,
53
+ credential_resolver=EnvCredentialResolver(env),
54
+ embed_provider=embed,
55
+ )
56
+
57
+
58
+ class BoundTools:
59
+ """Compiled YAML tools. Call them directly, or adapt to an agent framework.
60
+
61
+ >>> from vectorsmith import connect
62
+ >>> vs = connect("tools.yaml")
63
+ >>> await vs.call("search_invoices", {"query": "Globex", "limit": 3})
64
+ >>> tools = vs.as_langchain() # pip install 'vectorsmith[langchain]'
65
+ """
66
+
67
+ def __init__(self, engines: Sequence[Engine]) -> None:
68
+ self._engines = list(engines)
69
+ self._by_name: dict[str, Engine] = {}
70
+ schemas: list[dict[str, Any]] = []
71
+ for engine in self._engines:
72
+ for schema in engine.project.mcp_tool_schemas():
73
+ name = str(schema["name"])
74
+ self._by_name[name] = engine
75
+ schemas.append(schema)
76
+ self._schemas = schemas
77
+
78
+ @property
79
+ def names(self) -> list[str]:
80
+ return [str(s["name"]) for s in self._schemas]
81
+
82
+ @property
83
+ def schemas(self) -> list[dict[str, Any]]:
84
+ """MCP tool schemas (``name``, ``description``, ``inputSchema``)."""
85
+ return list(self._schemas)
86
+
87
+ def as_anthropic(self) -> list[dict[str, Any]]:
88
+ """Tool defs for ``anthropic.Anthropic().messages.create(tools=...)``."""
89
+ return [
90
+ {
91
+ "name": s["name"],
92
+ "description": s.get("description") or s["name"],
93
+ "input_schema": s.get("inputSchema") or {"type": "object", "properties": {}},
94
+ }
95
+ for s in self._schemas
96
+ ]
97
+
98
+ def as_langchain(self) -> Any:
99
+ """LangChain ``StructuredTool`` list. Requires ``vectorsmith[langchain]``."""
100
+ from vectorsmith.langchain_tools import toolset_from_bound
101
+
102
+ return toolset_from_bound(self)
103
+
104
+ def as_openai_agents(self) -> Any:
105
+ """OpenAI Agents SDK ``FunctionTool`` list. Requires ``vectorsmith[openai-agents]``."""
106
+ from vectorsmith.openai_agents import toolset_from_bound
107
+
108
+ return toolset_from_bound(self)
109
+
110
+ async def call(self, name: str, args: Mapping[str, Any] | None = None) -> dict[str, Any]:
111
+ engine = self._by_name.get(name)
112
+ if engine is None:
113
+ known = ", ".join(self.names) or "(none)"
114
+ raise KeyError(f"unknown tool {name!r}; loaded: {known}")
115
+ clean = {k: v for k, v in dict(args or {}).items() if v is not None}
116
+ result = await engine.call(
117
+ name, clean, ctx=CallContext(request_id=str(uuid.uuid4()))
118
+ )
119
+ return result.model_dump()
120
+
121
+ async def aclose(self) -> None:
122
+ for engine in self._engines:
123
+ await engine.aclose()
124
+
125
+
126
+ def connect(
127
+ *sources: str | Path | dict[str, Any],
128
+ env: Mapping[str, str] | None = None,
129
+ env_file: str | Path | None = None,
130
+ ) -> BoundTools:
131
+ """Compile one or more tools.yaml files. No MCP subprocess.
132
+
133
+ Use this when you want ``await vs.call(...)`` or a non-LangChain adapter.
134
+ LangChain / LangGraph apps can keep using ``load_tools``.
135
+ """
136
+ if not sources:
137
+ raise ValueError("connect requires at least one tools.yaml path")
138
+ merged = _merge_env(env, env_file)
139
+ return BoundTools([_one_engine(source, merged) for source in sources])
@@ -0,0 +1 @@
1
+ """VectorSmith CLI."""