langstitch-sdk 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.
- langstitch_sdk-0.1.0/.github/workflows/ci.yml +61 -0
- langstitch_sdk-0.1.0/.github/workflows/publish.yml +47 -0
- langstitch_sdk-0.1.0/.gitignore +9 -0
- langstitch_sdk-0.1.0/LICENSE +21 -0
- langstitch_sdk-0.1.0/PKG-INFO +396 -0
- langstitch_sdk-0.1.0/README.md +348 -0
- langstitch_sdk-0.1.0/pyproject.toml +76 -0
- langstitch_sdk-0.1.0/src/langstitch/__init__.py +190 -0
- langstitch_sdk-0.1.0/src/langstitch/_decorators.py +59 -0
- langstitch_sdk-0.1.0/src/langstitch/_version.py +3 -0
- langstitch_sdk-0.1.0/src/langstitch/agents.py +60 -0
- langstitch_sdk-0.1.0/src/langstitch/app.py +113 -0
- langstitch_sdk-0.1.0/src/langstitch/cli.py +181 -0
- langstitch_sdk-0.1.0/src/langstitch/config.py +360 -0
- langstitch_sdk-0.1.0/src/langstitch/context.py +308 -0
- langstitch_sdk-0.1.0/src/langstitch/graph.py +144 -0
- langstitch_sdk-0.1.0/src/langstitch/guardrails.py +63 -0
- langstitch_sdk-0.1.0/src/langstitch/hitl.py +39 -0
- langstitch_sdk-0.1.0/src/langstitch/mcp.py +194 -0
- langstitch_sdk-0.1.0/src/langstitch/nodes.py +43 -0
- langstitch_sdk-0.1.0/src/langstitch/persona.py +42 -0
- langstitch_sdk-0.1.0/src/langstitch/policy.py +44 -0
- langstitch_sdk-0.1.0/src/langstitch/providers.py +234 -0
- langstitch_sdk-0.1.0/src/langstitch/registries.py +230 -0
- langstitch_sdk-0.1.0/src/langstitch/registry.py +302 -0
- langstitch_sdk-0.1.0/src/langstitch/scaffold.py +489 -0
- langstitch_sdk-0.1.0/src/langstitch/server.py +161 -0
- langstitch_sdk-0.1.0/src/langstitch/services.py +450 -0
- langstitch_sdk-0.1.0/src/langstitch/skills.py +45 -0
- langstitch_sdk-0.1.0/src/langstitch/tools.py +58 -0
- langstitch_sdk-0.1.0/src/langstitch/tracing.py +362 -0
- langstitch_sdk-0.1.0/tests/conftest.py +14 -0
- langstitch_sdk-0.1.0/tests/test_config.py +65 -0
- langstitch_sdk-0.1.0/tests/test_config_json.py +87 -0
- langstitch_sdk-0.1.0/tests/test_context.py +126 -0
- langstitch_sdk-0.1.0/tests/test_decorators.py +82 -0
- langstitch_sdk-0.1.0/tests/test_http_client.py +125 -0
- langstitch_sdk-0.1.0/tests/test_mcp.py +72 -0
- langstitch_sdk-0.1.0/tests/test_properties.py +75 -0
- langstitch_sdk-0.1.0/tests/test_providers.py +68 -0
- langstitch_sdk-0.1.0/tests/test_registries.py +79 -0
- langstitch_sdk-0.1.0/tests/test_scaffold_cli.py +55 -0
- langstitch_sdk-0.1.0/tests/test_services.py +175 -0
- langstitch_sdk-0.1.0/tests/test_tracing.py +121 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ['3.10', '3.11', '3.12', '3.13']
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install package (with extras)
|
|
27
|
+
run: python -m pip install --upgrade pip && pip install -e ".[dev,http,tracing]"
|
|
28
|
+
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: python -m pytest -q
|
|
31
|
+
|
|
32
|
+
build:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
needs: test
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
|
|
38
|
+
- uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: '3.12'
|
|
41
|
+
|
|
42
|
+
- name: Install build tooling
|
|
43
|
+
run: python -m pip install --upgrade pip build twine
|
|
44
|
+
|
|
45
|
+
- name: Build sdist + wheel
|
|
46
|
+
run: python -m build
|
|
47
|
+
|
|
48
|
+
- name: Validate distribution metadata
|
|
49
|
+
run: python -m twine check dist/*
|
|
50
|
+
|
|
51
|
+
- name: Verify wheel installs and CLI runs
|
|
52
|
+
run: |
|
|
53
|
+
python -m venv /tmp/venv
|
|
54
|
+
/tmp/venv/bin/pip install dist/*.whl
|
|
55
|
+
/tmp/venv/bin/langstitch version
|
|
56
|
+
|
|
57
|
+
- name: Upload distribution artifacts
|
|
58
|
+
uses: actions/upload-artifact@v4
|
|
59
|
+
with:
|
|
60
|
+
name: langstitch-dist
|
|
61
|
+
path: dist/*
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
# Publishes the langstitch SDK to PyPI when a tag like `v0.1.0` is pushed (or a
|
|
4
|
+
# matching GitHub Release is published). Uses PyPI Trusted Publishing (OIDC) —
|
|
5
|
+
# no API token secret required once the project is configured on PyPI to trust
|
|
6
|
+
# this workflow.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags:
|
|
11
|
+
- 'v*'
|
|
12
|
+
release:
|
|
13
|
+
types: [published]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: '3.12'
|
|
26
|
+
- name: Build distribution
|
|
27
|
+
run: python -m pip install --upgrade pip build twine && python -m build && python -m twine check dist/*
|
|
28
|
+
- uses: actions/upload-artifact@v4
|
|
29
|
+
with:
|
|
30
|
+
name: langstitch-dist
|
|
31
|
+
path: dist/*
|
|
32
|
+
|
|
33
|
+
publish:
|
|
34
|
+
needs: build
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
environment: pypi
|
|
37
|
+
permissions:
|
|
38
|
+
id-token: write # required for PyPI Trusted Publishing
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/download-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: langstitch-dist
|
|
43
|
+
path: dist
|
|
44
|
+
- name: Publish to PyPI
|
|
45
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
46
|
+
with:
|
|
47
|
+
packages-dir: dist
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LangStitch
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: langstitch-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LangStitch SDK — decorators, config, and CLI for building LangGraph applications with a clean project scaffold.
|
|
5
|
+
Project-URL: Homepage, https://langstitch.com
|
|
6
|
+
Project-URL: Documentation, https://langstitch.com/docs/
|
|
7
|
+
Project-URL: Repository, https://github.com/LangStitch/langstitch-sdk
|
|
8
|
+
Author-email: LangStitch <connect@langstitch.com>
|
|
9
|
+
Maintainer-email: LangStitch <connect@langstitch.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,langchain,langgraph,langstitch,llm,sdk
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: fastapi>=0.115.0; extra == 'all'
|
|
27
|
+
Requires-Dist: httpx>=0.27.0; extra == 'all'
|
|
28
|
+
Requires-Dist: langchain-core>=0.3.0; extra == 'all'
|
|
29
|
+
Requires-Dist: langchain>=0.3.0; extra == 'all'
|
|
30
|
+
Requires-Dist: langgraph>=0.4.0; extra == 'all'
|
|
31
|
+
Requires-Dist: langsmith>=0.1.0; extra == 'all'
|
|
32
|
+
Requires-Dist: uvicorn[standard]>=0.32.0; extra == 'all'
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
35
|
+
Provides-Extra: graph
|
|
36
|
+
Requires-Dist: langchain-core>=0.3.0; extra == 'graph'
|
|
37
|
+
Requires-Dist: langgraph>=0.4.0; extra == 'graph'
|
|
38
|
+
Provides-Extra: http
|
|
39
|
+
Requires-Dist: httpx>=0.27.0; extra == 'http'
|
|
40
|
+
Provides-Extra: llm
|
|
41
|
+
Requires-Dist: langchain>=0.3.0; extra == 'llm'
|
|
42
|
+
Provides-Extra: server
|
|
43
|
+
Requires-Dist: fastapi>=0.115.0; extra == 'server'
|
|
44
|
+
Requires-Dist: uvicorn[standard]>=0.32.0; extra == 'server'
|
|
45
|
+
Provides-Extra: tracing
|
|
46
|
+
Requires-Dist: langsmith>=0.1.0; extra == 'tracing'
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# LangStitch SDK
|
|
50
|
+
|
|
51
|
+
A small, batteries-included Python SDK for building LangGraph applications with a
|
|
52
|
+
clean, conventional project structure. You describe components with decorators,
|
|
53
|
+
configure the app with YAML, and run it with one command.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install langstitch-sdk # core (PyYAML only)
|
|
57
|
+
pip install "langstitch-sdk[all]" # + FastAPI server + LangGraph
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
> **Prefer a visual workflow?** The LangStitch IDE ships an **SDK Component Designer** for
|
|
61
|
+
> authoring custom nodes, connectors, and adaptors without writing registration code — define
|
|
62
|
+
> ports, a typed config schema, and a safe Python codegen template, then export the component as
|
|
63
|
+
> a portable `.component.json` or publish it to the
|
|
64
|
+
> [marketplace](https://marketplace.langstitch.com). See the
|
|
65
|
+
> [Component Designer docs](https://langstitch.com/docs/#components). This README covers the
|
|
66
|
+
> **code-first Python SDK**.
|
|
67
|
+
|
|
68
|
+
### Use it as a dependency
|
|
69
|
+
|
|
70
|
+
In your `pyproject.toml`:
|
|
71
|
+
|
|
72
|
+
```toml
|
|
73
|
+
[project]
|
|
74
|
+
dependencies = [
|
|
75
|
+
"langstitch-sdk>=0.1.0", # from PyPI
|
|
76
|
+
# extras: "langstitch-sdk[server,graph,llm,http]>=0.1.0"
|
|
77
|
+
]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Declare it in your `pyproject.toml`:
|
|
81
|
+
|
|
82
|
+
```toml
|
|
83
|
+
[project]
|
|
84
|
+
dependencies = [
|
|
85
|
+
"langstitch-sdk>=0.1.0",
|
|
86
|
+
]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Quick start
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
langstitch new my-agent
|
|
93
|
+
cd my-agent
|
|
94
|
+
pip install -e .
|
|
95
|
+
python -m app # bootstrap + print app info
|
|
96
|
+
langstitch run # start the API server
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Decorators
|
|
100
|
+
|
|
101
|
+
| Decorator | Purpose |
|
|
102
|
+
| --- | --- |
|
|
103
|
+
| `@graph_node` | Register a node handler (`state -> dict`). |
|
|
104
|
+
| `@graph` | Register a graph builder (`entrypoint=True` for the root, `parent=...` for subgraphs). |
|
|
105
|
+
| `@skill` | Register a reusable capability. |
|
|
106
|
+
| `@input_guardrail` / `@output_guardrail` | Validate inbound requests / outbound responses. |
|
|
107
|
+
| `@business_policy` | Register an organizational rule (evaluated by `priority`). |
|
|
108
|
+
| `@persona` | Register an agent identity / system prompt. |
|
|
109
|
+
| `@configuration` | Bind a section of `application.yaml` to a dataclass. |
|
|
110
|
+
| `@langstitch_graph_server` | Turn a class into a runnable graph API server (`protocol`, `port`, `name`, `properties`). |
|
|
111
|
+
| `@tool` | Register a callable an LLM can invoke (`roles`, `tags`, `input_schema`). |
|
|
112
|
+
| `@worker_agent` | Register a delegatable sub-agent (`role`, `tools`, `persona`). |
|
|
113
|
+
| `@langstitch_mcp_server` | Mark the MCP server class + transport (`protocol="stdio"\|"sse"\|"streamable-http"\|"http"\|"websocket"`, `properties`). |
|
|
114
|
+
| `@mcp_tool` | Expose a callable as an MCP tool (`name`, `roles`, `description`). |
|
|
115
|
+
| `@mcp_resource` | Expose a readable MCP resource (`name`, `uri`, `mime_type`). |
|
|
116
|
+
| `@mcp_prompt` | Expose a reusable MCP prompt (`name`, `description`, `arguments`). |
|
|
117
|
+
|
|
118
|
+
Every decorator works bare or parameterized:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from langstitch import skill
|
|
122
|
+
|
|
123
|
+
@skill
|
|
124
|
+
def echo(text: str) -> str:
|
|
125
|
+
return text
|
|
126
|
+
|
|
127
|
+
@skill(name="search", tools=["web"], tags=["retrieval"])
|
|
128
|
+
def web_search(query: str) -> list[str]:
|
|
129
|
+
...
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Configuration
|
|
133
|
+
|
|
134
|
+
Two YAML files at the project root drive an app:
|
|
135
|
+
|
|
136
|
+
- **`application.yaml`** — application configuration (app metadata, model, graph, server, custom sections).
|
|
137
|
+
- **`env.yaml`** — runtime environment variables, exported into `os.environ` (existing values win unless `override=True`). Nested keys flatten to `UPPER_SNAKE` (`openai.api_key` → `OPENAI_API_KEY`).
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from langstitch import load_config
|
|
141
|
+
|
|
142
|
+
cfg = load_config() # loads env.yaml then application config
|
|
143
|
+
print(cfg.name, cfg.get("server.port"))
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Precompiled in-memory config + JSON-path lookups
|
|
147
|
+
|
|
148
|
+
At startup `load_config()` parses the application config **once** into an
|
|
149
|
+
in-memory object (the runtime store). Use `get_config(path)` to read from it
|
|
150
|
+
with a JSON-path-lite syntax (dotted keys, `[index]`, optional leading `$`):
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from langstitch import get_config
|
|
154
|
+
|
|
155
|
+
get_config() # the whole AppConfig
|
|
156
|
+
get_config("server.port") # -> 9001 (scalar)
|
|
157
|
+
get_config("model") # -> {...} (nested object)
|
|
158
|
+
get_config("external_services.payments.auth.type")
|
|
159
|
+
get_config("items[0].name") # array index
|
|
160
|
+
get_config("missing.key", default="fallback")
|
|
161
|
+
get_config("server", as_json=True) # -> '{"host": ...}' (JSON string)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
If you keep the config as **`application.json`** it's loaded directly (no
|
|
165
|
+
YAML→JSON conversion) and `application.json` takes precedence over
|
|
166
|
+
`application.yaml`. Precompile once for fast startup:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
langstitch compile # application.yaml -> application.json
|
|
170
|
+
langstitch get server.port # resolve a path from the CLI
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Both server decorators accept `properties=` to pin the config file loaded at
|
|
174
|
+
startup (relative to the project root, or absolute). When omitted, discovery is
|
|
175
|
+
used (`application.json` preferred, else `application.yaml`):
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
@langstitch_graph_server(name="api", properties="application.yaml") # pin YAML
|
|
179
|
+
class Server: ...
|
|
180
|
+
|
|
181
|
+
@langstitch_mcp_server(protocol="stdio") # default: application.json then yaml
|
|
182
|
+
class MCPServer: ...
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from dataclasses import dataclass
|
|
187
|
+
from langstitch import configuration
|
|
188
|
+
|
|
189
|
+
@configuration(section="server")
|
|
190
|
+
@dataclass
|
|
191
|
+
class ServerConfig:
|
|
192
|
+
host: str = "0.0.0.0"
|
|
193
|
+
port: int = 8000
|
|
194
|
+
|
|
195
|
+
# after load_config(): ServerConfig._langstitch_instance is populated
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Base runtime helpers
|
|
199
|
+
|
|
200
|
+
Factory functions that read `application.yaml` / `env.yaml` so app code never
|
|
201
|
+
hand-builds clients:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from langstitch import (
|
|
205
|
+
get_config, get_env, get_secret, get_logger,
|
|
206
|
+
get_llm_provider, get_http_client, get_async_http_client,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
cfg = get_config() # cached AppConfig
|
|
210
|
+
log = get_logger(__name__) # level from LOG_LEVEL
|
|
211
|
+
llm = get_llm_provider() # chat model from model: section (needs [llm])
|
|
212
|
+
http = get_http_client() # httpx.Client from http: section (needs [http])
|
|
213
|
+
key = get_secret("openai_api_key") # env lookup with sensible fallbacks
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Heavy deps are optional extras: `pip install "langstitch-sdk[llm]"` (LangChain) and
|
|
217
|
+
`pip install "langstitch-sdk[http]"` (httpx). Without them the helpers raise a clear
|
|
218
|
+
install hint. The same helpers are available as methods on `LangStitchApp`
|
|
219
|
+
(`app.get_llm_provider()`, `app.get_http_client()`, ...).
|
|
220
|
+
|
|
221
|
+
### External services & `get_http_client("<service>")`
|
|
222
|
+
|
|
223
|
+
Declare downstream HTTP services in `application.yaml`:
|
|
224
|
+
|
|
225
|
+
```yaml
|
|
226
|
+
external_services:
|
|
227
|
+
payments:
|
|
228
|
+
serverUrl: https://api.payments.com # or server_url
|
|
229
|
+
basePath: /v1 # or base_path
|
|
230
|
+
timeout: 30
|
|
231
|
+
propagate_headers: [x-request-id, authorization]
|
|
232
|
+
auth:
|
|
233
|
+
type: bearer # none | basic | bearer | api_key | oauth2
|
|
234
|
+
token: ${PAYMENTS_TOKEN}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from langstitch import get_http_client, set_request_headers
|
|
239
|
+
|
|
240
|
+
# In request middleware, record inbound headers once:
|
|
241
|
+
set_request_headers(request.headers)
|
|
242
|
+
|
|
243
|
+
api = get_http_client("payments") # base_url, timeout, auth + propagated headers wired in
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The returned `ServiceClient` covers all HTTP verbs with `{path}` templating and
|
|
247
|
+
per-request header/query merging (auth + propagated headers stay applied):
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
api.get("/users/{id}", path_params={"id": 7}, params={"expand": "wallet"})
|
|
251
|
+
api.post("/users", json={"name": "Ada"}, headers={"X-Trace": "1"})
|
|
252
|
+
api.put("/users/{id}", path_params={"id": 7}, json={...})
|
|
253
|
+
api.patch("/users/{id}", path_params={"id": 7}, json={...})
|
|
254
|
+
api.delete("/users/{id}", path_params={"id": 7})
|
|
255
|
+
api.request("OPTIONS", "/users")
|
|
256
|
+
|
|
257
|
+
api.set_header("X-Tenant", "acme") # mutate default headers
|
|
258
|
+
api.add_headers({"X-Region": "eu"})
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
`get_async_http_client("payments")` returns the awaitable `AsyncServiceClient`
|
|
262
|
+
equivalent. Pass `raw=True` to either for the underlying httpx client.
|
|
263
|
+
|
|
264
|
+
Auth types and their options (string values support `${ENV_VAR}` interpolation):
|
|
265
|
+
|
|
266
|
+
| `auth.type` | Options | Effect |
|
|
267
|
+
| --- | --- | --- |
|
|
268
|
+
| `none` | — | no credentials |
|
|
269
|
+
| `basic` | `username`, `password` | `Authorization: Basic <b64>` |
|
|
270
|
+
| `bearer` | `token` | `Authorization: Bearer <token>` |
|
|
271
|
+
| `api_key` | `name` (default `X-API-Key`), `value`, `in` (`header`\|`query`) | header or query param |
|
|
272
|
+
| `oauth2` | `token_url`, `client_id`, `client_secret`, `scope?`, `audience?` | client-credentials; token fetched + cached/refreshed automatically |
|
|
273
|
+
|
|
274
|
+
`propagate_headers` forwards the listed inbound request headers (case-insensitive)
|
|
275
|
+
onto the outbound client. `get_async_http_client("<service>")` is the async variant.
|
|
276
|
+
|
|
277
|
+
## Dynamic registries & graph-server internal tools
|
|
278
|
+
|
|
279
|
+
Tools and worker agents are **not eagerly loaded** when a request arrives. The
|
|
280
|
+
registries hold cheap *specs* and refresh themselves automatically when anything
|
|
281
|
+
new registers (and on demand via `refresh_registries()`); the actual callables
|
|
282
|
+
are materialized only when a node selects them.
|
|
283
|
+
|
|
284
|
+
The graph server exposes introspection helpers (each hits the live registries):
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
Server.get_all_tools() # [ToolSpec, ...]
|
|
288
|
+
Server.get_all_worker_agents() # [AgentSpec, ...]
|
|
289
|
+
Server.get_input_guardrails()
|
|
290
|
+
Server.get_output_guardrails()
|
|
291
|
+
Server.get_skills(); Server.get_policies(); Server.get_personas()
|
|
292
|
+
Server.get_tool("now"); Server.get_worker_agent("researcher")
|
|
293
|
+
Server.refresh_registries()
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The same accessors are module-level functions (`langstitch.get_all_tools()`, ...).
|
|
297
|
+
|
|
298
|
+
## Hierarchical context (no parent pollution)
|
|
299
|
+
|
|
300
|
+
Every LLM call or sub-agent call runs in a **temporary child context**. The child
|
|
301
|
+
can accumulate tool-call messages and scratch reasoning freely; when the call
|
|
302
|
+
finishes, **only the final output** is merged back into the parent — so parents
|
|
303
|
+
stay small no matter how deep the call tree gets.
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
from langstitch import Context, run_llm, run_worker_agent
|
|
307
|
+
|
|
308
|
+
ctx = Context(data={"question": "..."}, messages=[...])
|
|
309
|
+
|
|
310
|
+
def call_model(llm_ctx):
|
|
311
|
+
# llm_ctx.tools were selected (by tag/name/role) and materialized just for
|
|
312
|
+
# this call; llm_ctx.system holds the resolved persona.
|
|
313
|
+
return model.invoke(llm_ctx.messages, tools=llm_ctx.tools)
|
|
314
|
+
|
|
315
|
+
answer = run_llm(ctx, call_model, persona="assistant", tool_tags=["search"], key="answer")
|
|
316
|
+
# ctx.data["answer"] is set; the child's tool traffic + scratch were discarded.
|
|
317
|
+
|
|
318
|
+
# Delegate to a sub-agent (runs with only its allowed tools, isolated context):
|
|
319
|
+
findings = run_worker_agent(ctx, "researcher", carry=["question"])
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
`ContextBuilder` does the lazy selection; `Context.scope(...)` / `ContextScope`
|
|
323
|
+
give you the raw building blocks if you need finer control.
|
|
324
|
+
|
|
325
|
+
## Building & running a graph
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
from langstitch import LangStitchApp
|
|
329
|
+
|
|
330
|
+
app = LangStitchApp.bootstrap()
|
|
331
|
+
graph = app.build_graph() # compiles to a LangGraph StateGraph
|
|
332
|
+
result = app.invoke({"messages": [{"role": "user", "content": "hi"}]})
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Tracing, logging & LangSmith
|
|
336
|
+
|
|
337
|
+
Optional observability via `langstitch.tracing` (install `pip install "langstitch-sdk[tracing]"`).
|
|
338
|
+
|
|
339
|
+
### Configuration
|
|
340
|
+
|
|
341
|
+
```yaml
|
|
342
|
+
# application.yaml
|
|
343
|
+
tracing:
|
|
344
|
+
enabled: true
|
|
345
|
+
project: my-agent-project
|
|
346
|
+
log_format: json # text | json
|
|
347
|
+
register_on_build: true # upsert LangSmith project on build_graph()
|
|
348
|
+
trace_nodes: true
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Environment variables (`LANGSMITH_API_KEY`, `LANGCHAIN_TRACING_V2=true`, `LANGCHAIN_PROJECT`) are applied automatically when tracing is enabled.
|
|
352
|
+
|
|
353
|
+
### Register a graph with LangSmith
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
from langstitch import LangStitchApp, configure_tracing, register_graph
|
|
357
|
+
|
|
358
|
+
configure_tracing()
|
|
359
|
+
app = LangStitchApp.bootstrap()
|
|
360
|
+
app.build_graph() # registers entrypoint when tracing.register_on_build is true
|
|
361
|
+
print(app.info()["registered_graphs"])
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
CLI:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
langstitch register # register entrypoint graph (needs app package)
|
|
368
|
+
langstitch register --describe-only # metadata only, no LangGraph compile
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Runtime agent smoke test
|
|
372
|
+
|
|
373
|
+
The repo ships `runtime/basic_agent.py` — a minimal SDK graph with optional LangSmith registration:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
python runtime/basic_agent.py
|
|
377
|
+
# {"ok": true, "tracing": {"registered": true, ...}}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## CLI
|
|
381
|
+
|
|
382
|
+
```text
|
|
383
|
+
langstitch new <name> [--dir PATH] [--force] scaffold a project
|
|
384
|
+
langstitch info [--root PATH] load config + list components
|
|
385
|
+
langstitch run [--root PATH] [--host] [--port] start the API server
|
|
386
|
+
langstitch register [--root PATH] [--describe-only] LangSmith graph registration
|
|
387
|
+
langstitch compile application.yaml -> application.json
|
|
388
|
+
langstitch get <json.path> resolve config path
|
|
389
|
+
langstitch version
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Status
|
|
393
|
+
|
|
394
|
+
Phase 1 (initial release): decorators, registry, YAML config, scaffolding CLI,
|
|
395
|
+
and an optional FastAPI server. LangGraph and FastAPI are optional extras so the
|
|
396
|
+
core stays lightweight and importable anywhere (including codegen).
|