Skip to content

Commit 07d14c1

Browse files
authored
Merge branch 'main' into jb/add-docs-link
2 parents 34804d5 + e87832b commit 07d14c1

14 files changed

Lines changed: 1463 additions & 114 deletions

.github/workflows/ci.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
runs-on: ubuntu-latest
1616
strategy:
1717
matrix:
18-
python-version: ["3.10", "3.11", "3.12", "3.13"]
18+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
1919

2020
steps:
2121
- uses: actions/checkout@v4
@@ -39,7 +39,7 @@ jobs:
3939

4040
strategy:
4141
matrix:
42-
python-version: ["3.10", "3.11", "3.12", "3.13"]
42+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
4343

4444
steps:
4545
- uses: actions/checkout@v4
@@ -59,7 +59,7 @@ jobs:
5959
runs-on: ubuntu-latest
6060
strategy:
6161
matrix:
62-
python-version: ["3.10", "3.11", "3.12", "3.13"]
62+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
6363

6464
steps:
6565
- uses: actions/checkout@v4
@@ -81,7 +81,7 @@ jobs:
8181

8282
strategy:
8383
matrix:
84-
python-version: ["3.10", "3.11", "3.12", "3.13"]
84+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
8585

8686
steps:
8787
- uses: actions/checkout@v4
@@ -101,7 +101,7 @@ jobs:
101101
runs-on: ubuntu-latest
102102
strategy:
103103
matrix:
104-
python-version: ["3.10", "3.11", "3.12", "3.13"]
104+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
105105

106106
steps:
107107
- uses: actions/checkout@v4
@@ -123,7 +123,7 @@ jobs:
123123

124124
strategy:
125125
matrix:
126-
python-version: ["3.10", "3.11", "3.12", "3.13"]
126+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
127127

128128
steps:
129129
- uses: actions/checkout@v4
@@ -143,7 +143,7 @@ jobs:
143143
runs-on: ubuntu-latest
144144
strategy:
145145
matrix:
146-
python-version: ["3.10", "3.11", "3.12", "3.13"]
146+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
147147

148148
steps:
149149
- uses: actions/checkout@v4
@@ -165,7 +165,7 @@ jobs:
165165

166166
strategy:
167167
matrix:
168-
python-version: ["3.10", "3.11", "3.12", "3.13"]
168+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
169169

170170
steps:
171171
- uses: actions/checkout@v4

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"packages/sdk/server-ai": "0.17.0",
3-
"packages/ai-providers/server-ai-langchain": "0.4.0",
3+
"packages/ai-providers/server-ai-langchain": "0.4.1",
44
"packages/ai-providers/server-ai-openai": "0.3.0",
55
"packages/optimization": "0.1.0"
66
}

packages/ai-providers/server-ai-langchain/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to the LaunchDarkly Python AI LangChain provider package will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
44

5+
## [0.4.1](https://github.com/launchdarkly/python-server-sdk-ai/compare/launchdarkly-server-sdk-ai-langchain-0.4.0...launchdarkly-server-sdk-ai-langchain-0.4.1) (2026-04-07)
6+
7+
8+
### Bug Fixes
9+
10+
* Fixes agent graph tool calls in fanned out graphs ([#123](https://github.com/launchdarkly/python-server-sdk-ai/issues/123)) ([c140410](https://github.com/launchdarkly/python-server-sdk-ai/commit/c14041090643d1261881343fdd70b4079997b773))
11+
512
## [0.4.0](https://github.com/launchdarkly/python-server-sdk-ai/compare/launchdarkly-server-sdk-ai-langchain-0.3.2...launchdarkly-server-sdk-ai-langchain-0.4.0) (2026-04-02)
613

714

packages/ai-providers/server-ai-langchain/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "launchdarkly-server-sdk-ai-langchain"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
description = "LaunchDarkly AI SDK LangChain Provider"
55
authors = [{name = "LaunchDarkly", email = "dev@launchdarkly.com"}]
66
license = {text = "Apache-2.0"}
@@ -15,6 +15,7 @@ classifiers = [
1515
"Programming Language :: Python :: 3.11",
1616
"Programming Language :: Python :: 3.12",
1717
"Programming Language :: Python :: 3.13",
18+
"Programming Language :: Python :: 3.14",
1819
"Topic :: Software Development",
1920
"Topic :: Software Development :: Libraries",
2021
]

packages/ai-providers/server-ai-langchain/src/ldai_langchain/langchain_helper.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import inspect
12
from typing import Any, Dict, List, Optional, Union
23

34
from langchain_core.language_models.chat_models import BaseChatModel
@@ -82,6 +83,41 @@ def create_langchain_model(ai_config: AIConfigKind) -> BaseChatModel:
8283
)
8384

8485

86+
def _iter_valid_tools(
87+
tool_definitions: List[Dict[str, Any]],
88+
tool_registry: ToolRegistry,
89+
) -> List[tuple]:
90+
"""
91+
Filter LD tool definitions against a registry, returning (name, td) pairs for each
92+
valid function tool that has a callable implementation. Built-in provider tools and
93+
tools missing from the registry are skipped with a warning.
94+
"""
95+
valid = []
96+
for td in tool_definitions:
97+
if not isinstance(td, dict):
98+
continue
99+
100+
tool_type = td.get('type')
101+
if tool_type and tool_type != 'function':
102+
log.warning(
103+
f"Built-in tool '{tool_type}' is not reliably supported via LangChain and will be skipped. "
104+
"Use a provider-specific runner to use built-in provider tools."
105+
)
106+
continue
107+
108+
name = td.get('name')
109+
if not name:
110+
continue
111+
112+
if name not in tool_registry:
113+
log.warning(f"Tool '{name}' is defined in the AI config but was not found in the tool registry; skipping.")
114+
continue
115+
116+
valid.append((name, td))
117+
118+
return valid
119+
120+
85121
def build_tools(ai_config: AIConfigKind, tool_registry: ToolRegistry) -> List[Any]:
86122
"""
87123
Return callables from the registry for each tool defined in the AI config.
@@ -114,6 +150,39 @@ def build_tools(ai_config: AIConfigKind, tool_registry: ToolRegistry) -> List[An
114150
return tools
115151

116152

153+
def build_structured_tools(ai_config: AIConfigKind, tool_registry: ToolRegistry) -> List[Any]:
154+
"""
155+
Build a list of LangChain StructuredTool instances from LD tool definitions and a registry.
156+
157+
Tools found in the registry are wrapped as StructuredTool using the LD config key as the
158+
tool name so the model's tool calls match ToolNode lookup. Async callables use ``coroutine=``
159+
so LangGraph invokes them correctly. Built-in provider tools and tools missing from the
160+
registry are skipped with a warning.
161+
162+
:param ai_config: The LaunchDarkly AI configuration
163+
:param tool_registry: Registry mapping tool names to callable implementations
164+
:return: List of StructuredTool instances ready to pass to langchain.agents.create_agent
165+
"""
166+
from langchain_core.tools import StructuredTool
167+
168+
config_dict = ai_config.to_dict()
169+
model_dict = config_dict.get('model') or {}
170+
parameters = dict(model_dict.get('parameters') or {})
171+
tool_definitions = parameters.pop('tools', []) or []
172+
173+
tools = []
174+
for name, td in _iter_valid_tools(tool_definitions, tool_registry):
175+
fn = tool_registry[name]
176+
raw_desc = td.get('description') if isinstance(td.get('description'), str) else ''
177+
description = raw_desc.strip() or (getattr(fn, '__doc__', None) or '').strip() or f'Tool {name}'
178+
if inspect.iscoroutinefunction(fn):
179+
tool = StructuredTool.from_function(coroutine=fn, name=name, description=description)
180+
else:
181+
tool = StructuredTool.from_function(fn, name=name, description=description)
182+
tools.append(tool)
183+
return tools
184+
185+
117186
def get_ai_usage_from_response(response: Any) -> Optional[TokenUsage]:
118187
"""
119188
Extract token usage from a LangChain response.

0 commit comments

Comments
 (0)