langchain-exaone 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.
- langchain_exaone-0.1.0/.gitignore +18 -0
- langchain_exaone-0.1.0/LICENSE +21 -0
- langchain_exaone-0.1.0/PKG-INFO +133 -0
- langchain_exaone-0.1.0/README.md +120 -0
- langchain_exaone-0.1.0/examples/basic.py +10 -0
- langchain_exaone-0.1.0/pyproject.toml +42 -0
- langchain_exaone-0.1.0/src/langchain_exaone/__init__.py +5 -0
- langchain_exaone-0.1.0/src/langchain_exaone/chat_models.py +829 -0
- langchain_exaone-0.1.0/src/langchain_exaone/py.typed +1 -0
- langchain_exaone-0.1.0/tests/test_chat_models.py +852 -0
- langchain_exaone-0.1.0/tests/test_standard.py +28 -0
- langchain_exaone-0.1.0/uv.lock +2194 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EXAONE Integrations contributors
|
|
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,133 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: langchain-exaone
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LangChain chat model integration for the EXAONE API
|
|
5
|
+
Project-URL: Repository, https://github.com/ykoh42/exaone-integrations
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: httpx<1,>=0.27
|
|
10
|
+
Requires-Dist: langchain-core<2,>=1.7.5
|
|
11
|
+
Requires-Dist: pydantic<3,>=2
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# `langchain-exaone`
|
|
15
|
+
|
|
16
|
+
Community-maintained LangChain chat model integration for the EXAONE API.
|
|
17
|
+
|
|
18
|
+
This package connects LangChain applications to the K-EXAONE 2.5 Chat Completions API. It implements LangChain's `BaseChatModel` interface and supports synchronous and asynchronous calls, streaming, tool calls, structured-output binding, usage metadata, and EXAONE reasoning history.
|
|
19
|
+
|
|
20
|
+
This project is not affiliated with or endorsed by LG AI Research or LangChain.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
This package is not published to PyPI yet. From a checkout of this repository, install it with:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install ./packages/langchain-exaone-py
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
After the first release, install it from PyPI with:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install -U langchain-exaone
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Set `EXAONE_API_KEY` in the environment, then create the model:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from langchain_exaone import ChatEXAONE
|
|
40
|
+
|
|
41
|
+
model = ChatEXAONE()
|
|
42
|
+
message = model.invoke("안녕하세요. K-EXAONE을 소개해 주세요.")
|
|
43
|
+
print(message.text)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The package does not load `.env` files. Use your application's secret-management setup to place the API key in the process environment, or pass `api_key` directly.
|
|
47
|
+
|
|
48
|
+
## Serialization
|
|
49
|
+
|
|
50
|
+
`ChatEXAONE` does not opt into LangChain object serialization. LangChain's deserializer only allows registered integration classes by default, and this standalone package is not registered in `langchain-core`. The provider does not modify LangChain's global serialization registry. Create the model from application configuration when starting your process; its API key is held in a `SecretStr` and omitted from the model representation.
|
|
51
|
+
|
|
52
|
+
## Reasoning history
|
|
53
|
+
|
|
54
|
+
EXAONE returns reasoning separately from final content. The provider exposes it on `AIMessage.additional_kwargs["reasoning_content"]` and includes it when an assistant message is sent back in later turns. Keep the original `AIMessage` in conversation history so reasoning and tool calls remain attached to their assistant turn.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from langchain_core.messages import HumanMessage
|
|
58
|
+
from langchain_exaone import ChatEXAONE
|
|
59
|
+
|
|
60
|
+
model = ChatEXAONE(reasoning_effort="low")
|
|
61
|
+
history = [HumanMessage("2 + 2는 얼마야?")]
|
|
62
|
+
answer = model.invoke(history)
|
|
63
|
+
history.append(answer)
|
|
64
|
+
history.append(HumanMessage("방금 답을 확인해줘."))
|
|
65
|
+
follow_up = model.invoke(history)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use `model.astream(...)` for asynchronous streaming. Streaming chunks retain reasoning in `additional_kwargs`, while text remains in the normal message content field.
|
|
69
|
+
|
|
70
|
+
LangChain's streaming interface represents one completion per run. If the API returns multiple choices, such as when `n` is greater than `1` in `model_kwargs`, `stream()` and `astream()` raise `ValueError` rather than merging separate candidates. Use `generate()` when you need multiple candidates.
|
|
71
|
+
|
|
72
|
+
## Tools and structured output
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from langchain_core.tools import tool
|
|
76
|
+
from langchain_exaone import ChatEXAONE
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@tool
|
|
80
|
+
def get_weather(city: str) -> str:
|
|
81
|
+
"""Get the current weather for a city."""
|
|
82
|
+
return f"Sunny in {city}."
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
model = ChatEXAONE().bind_tools([get_weather], tool_choice="auto")
|
|
86
|
+
response = model.invoke("서울 날씨를 알려줘.")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`with_structured_output()` uses LangChain's standard tool-based strategy. You can pass additional Chat Completions fields with `model_kwargs` or per-call keyword arguments. Their acceptance depends on the EXAONE endpoint.
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
| Option | Default | Description |
|
|
94
|
+
| --- | --- | --- |
|
|
95
|
+
| `model` | `k-exaone_v2.5` | EXAONE model ID |
|
|
96
|
+
| `api_key` | `EXAONE_API_KEY` | API credential |
|
|
97
|
+
| `base_url` | `https://api.exaone.ai/v1` | API base URL. Remote endpoints must use HTTPS; HTTP is allowed for loopback test servers. |
|
|
98
|
+
| `temperature` | `1.0` | Recommended EXAONE sampling value |
|
|
99
|
+
| `top_p` | `0.95` | Recommended EXAONE sampling value |
|
|
100
|
+
| `top_k` | `-1` | Disable top-k filtering, as in the API default |
|
|
101
|
+
| `presence_penalty` | `0.0` | Recommended EXAONE sampling value |
|
|
102
|
+
| `max_tokens` | API default | Maximum generated tokens, including reasoning |
|
|
103
|
+
| `reasoning_effort` | API default | `low`, `medium`, `high`, or `max` |
|
|
104
|
+
| `preserve_thinking` | `true` | Keep earlier assistant reasoning in multi-turn agent conversations |
|
|
105
|
+
| `chat_template_kwargs` | `{}` | Additional chat-template options; `preserve_thinking` is set from its dedicated option |
|
|
106
|
+
| `max_retries` | `2` | Retries for transient API errors before output is returned; set to `0` to disable |
|
|
107
|
+
| `timeout` | `None` | No provider-imposed HTTP timeout; pass a positive number to set one |
|
|
108
|
+
|
|
109
|
+
The API always enables reasoning. The provider preserves the returned `reasoning_content` on assistant messages and sends it with later turns; `preserve_thinking=True` also enables EXAONE's recommended `chat_template_kwargs.preserve_thinking` behavior. Keep returned assistant messages intact when continuing a conversation.
|
|
110
|
+
|
|
111
|
+
`max_tokens` is capped at the documented 524,288-token context limit. The provider cannot count an arbitrary prompt locally, so the API still validates the combined input and output token budget.
|
|
112
|
+
|
|
113
|
+
The provider retries HTTP 408, 429, 500, 502, 503, and 504 errors up to two times by default; set `max_retries=0` to disable retries. It honors the longer valid `Retry-After` or `Retry-After-Ms` delay. If neither header is present, the first retry waits at least one second to respect the API's per-second rate limit; a `max_retry_delay` below one second disables that retry. It does not retry after streaming output has started.
|
|
114
|
+
|
|
115
|
+
The EXAONE API is text-only. Image, audio, and other non-text input blocks are rejected explicitly.
|
|
116
|
+
|
|
117
|
+
See the [K-EXAONE 2.5 API reference](https://docs.exaone.ai/ko/docs/k-exaone-2.5) for the model's request parameters and limits.
|
|
118
|
+
|
|
119
|
+
## Examples
|
|
120
|
+
|
|
121
|
+
See [`examples/basic.py`](examples/basic.py) for a runnable example. The package does not make API requests during import or construction.
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
uv sync --group test --group lint
|
|
127
|
+
uv run pytest
|
|
128
|
+
uv run ruff check .
|
|
129
|
+
uv run ruff format --check .
|
|
130
|
+
uv build
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Unit tests use synthetic responses and do not require API credentials.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# `langchain-exaone`
|
|
2
|
+
|
|
3
|
+
Community-maintained LangChain chat model integration for the EXAONE API.
|
|
4
|
+
|
|
5
|
+
This package connects LangChain applications to the K-EXAONE 2.5 Chat Completions API. It implements LangChain's `BaseChatModel` interface and supports synchronous and asynchronous calls, streaming, tool calls, structured-output binding, usage metadata, and EXAONE reasoning history.
|
|
6
|
+
|
|
7
|
+
This project is not affiliated with or endorsed by LG AI Research or LangChain.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
This package is not published to PyPI yet. From a checkout of this repository, install it with:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install ./packages/langchain-exaone-py
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
After the first release, install it from PyPI with:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install -U langchain-exaone
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Set `EXAONE_API_KEY` in the environment, then create the model:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from langchain_exaone import ChatEXAONE
|
|
27
|
+
|
|
28
|
+
model = ChatEXAONE()
|
|
29
|
+
message = model.invoke("안녕하세요. K-EXAONE을 소개해 주세요.")
|
|
30
|
+
print(message.text)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The package does not load `.env` files. Use your application's secret-management setup to place the API key in the process environment, or pass `api_key` directly.
|
|
34
|
+
|
|
35
|
+
## Serialization
|
|
36
|
+
|
|
37
|
+
`ChatEXAONE` does not opt into LangChain object serialization. LangChain's deserializer only allows registered integration classes by default, and this standalone package is not registered in `langchain-core`. The provider does not modify LangChain's global serialization registry. Create the model from application configuration when starting your process; its API key is held in a `SecretStr` and omitted from the model representation.
|
|
38
|
+
|
|
39
|
+
## Reasoning history
|
|
40
|
+
|
|
41
|
+
EXAONE returns reasoning separately from final content. The provider exposes it on `AIMessage.additional_kwargs["reasoning_content"]` and includes it when an assistant message is sent back in later turns. Keep the original `AIMessage` in conversation history so reasoning and tool calls remain attached to their assistant turn.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from langchain_core.messages import HumanMessage
|
|
45
|
+
from langchain_exaone import ChatEXAONE
|
|
46
|
+
|
|
47
|
+
model = ChatEXAONE(reasoning_effort="low")
|
|
48
|
+
history = [HumanMessage("2 + 2는 얼마야?")]
|
|
49
|
+
answer = model.invoke(history)
|
|
50
|
+
history.append(answer)
|
|
51
|
+
history.append(HumanMessage("방금 답을 확인해줘."))
|
|
52
|
+
follow_up = model.invoke(history)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use `model.astream(...)` for asynchronous streaming. Streaming chunks retain reasoning in `additional_kwargs`, while text remains in the normal message content field.
|
|
56
|
+
|
|
57
|
+
LangChain's streaming interface represents one completion per run. If the API returns multiple choices, such as when `n` is greater than `1` in `model_kwargs`, `stream()` and `astream()` raise `ValueError` rather than merging separate candidates. Use `generate()` when you need multiple candidates.
|
|
58
|
+
|
|
59
|
+
## Tools and structured output
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from langchain_core.tools import tool
|
|
63
|
+
from langchain_exaone import ChatEXAONE
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@tool
|
|
67
|
+
def get_weather(city: str) -> str:
|
|
68
|
+
"""Get the current weather for a city."""
|
|
69
|
+
return f"Sunny in {city}."
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
model = ChatEXAONE().bind_tools([get_weather], tool_choice="auto")
|
|
73
|
+
response = model.invoke("서울 날씨를 알려줘.")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`with_structured_output()` uses LangChain's standard tool-based strategy. You can pass additional Chat Completions fields with `model_kwargs` or per-call keyword arguments. Their acceptance depends on the EXAONE endpoint.
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
| Option | Default | Description |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| `model` | `k-exaone_v2.5` | EXAONE model ID |
|
|
83
|
+
| `api_key` | `EXAONE_API_KEY` | API credential |
|
|
84
|
+
| `base_url` | `https://api.exaone.ai/v1` | API base URL. Remote endpoints must use HTTPS; HTTP is allowed for loopback test servers. |
|
|
85
|
+
| `temperature` | `1.0` | Recommended EXAONE sampling value |
|
|
86
|
+
| `top_p` | `0.95` | Recommended EXAONE sampling value |
|
|
87
|
+
| `top_k` | `-1` | Disable top-k filtering, as in the API default |
|
|
88
|
+
| `presence_penalty` | `0.0` | Recommended EXAONE sampling value |
|
|
89
|
+
| `max_tokens` | API default | Maximum generated tokens, including reasoning |
|
|
90
|
+
| `reasoning_effort` | API default | `low`, `medium`, `high`, or `max` |
|
|
91
|
+
| `preserve_thinking` | `true` | Keep earlier assistant reasoning in multi-turn agent conversations |
|
|
92
|
+
| `chat_template_kwargs` | `{}` | Additional chat-template options; `preserve_thinking` is set from its dedicated option |
|
|
93
|
+
| `max_retries` | `2` | Retries for transient API errors before output is returned; set to `0` to disable |
|
|
94
|
+
| `timeout` | `None` | No provider-imposed HTTP timeout; pass a positive number to set one |
|
|
95
|
+
|
|
96
|
+
The API always enables reasoning. The provider preserves the returned `reasoning_content` on assistant messages and sends it with later turns; `preserve_thinking=True` also enables EXAONE's recommended `chat_template_kwargs.preserve_thinking` behavior. Keep returned assistant messages intact when continuing a conversation.
|
|
97
|
+
|
|
98
|
+
`max_tokens` is capped at the documented 524,288-token context limit. The provider cannot count an arbitrary prompt locally, so the API still validates the combined input and output token budget.
|
|
99
|
+
|
|
100
|
+
The provider retries HTTP 408, 429, 500, 502, 503, and 504 errors up to two times by default; set `max_retries=0` to disable retries. It honors the longer valid `Retry-After` or `Retry-After-Ms` delay. If neither header is present, the first retry waits at least one second to respect the API's per-second rate limit; a `max_retry_delay` below one second disables that retry. It does not retry after streaming output has started.
|
|
101
|
+
|
|
102
|
+
The EXAONE API is text-only. Image, audio, and other non-text input blocks are rejected explicitly.
|
|
103
|
+
|
|
104
|
+
See the [K-EXAONE 2.5 API reference](https://docs.exaone.ai/ko/docs/k-exaone-2.5) for the model's request parameters and limits.
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
See [`examples/basic.py`](examples/basic.py) for a runnable example. The package does not make API requests during import or construction.
|
|
109
|
+
|
|
110
|
+
## Development
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
uv sync --group test --group lint
|
|
114
|
+
uv run pytest
|
|
115
|
+
uv run ruff check .
|
|
116
|
+
uv run ruff format --check .
|
|
117
|
+
uv build
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Unit tests use synthetic responses and do not require API credentials.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Call EXAONE with the LangChain chat model integration."""
|
|
2
|
+
|
|
3
|
+
from langchain_exaone import ChatEXAONE
|
|
4
|
+
|
|
5
|
+
model = ChatEXAONE(reasoning_effort="low")
|
|
6
|
+
response = model.invoke("K-EXAONE을 한 문장으로 소개해 주세요.")
|
|
7
|
+
|
|
8
|
+
print(response.text)
|
|
9
|
+
print("Finish reason:", response.response_metadata.get("finish_reason"))
|
|
10
|
+
print("Usage:", response.usage_metadata)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.26"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "langchain-exaone"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "LangChain chat model integration for the EXAONE API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"httpx>=0.27,<1",
|
|
15
|
+
"langchain-core>=1.7.5,<2",
|
|
16
|
+
"pydantic>=2,<3",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Repository = "https://github.com/ykoh42/exaone-integrations"
|
|
21
|
+
|
|
22
|
+
[dependency-groups]
|
|
23
|
+
test = [
|
|
24
|
+
"langchain-tests==1.1.9",
|
|
25
|
+
"pytest>=9,<10",
|
|
26
|
+
"pytest-asyncio>=1,<2",
|
|
27
|
+
]
|
|
28
|
+
lint = ["ruff>=0.16,<0.18"]
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.wheel]
|
|
31
|
+
packages = ["src/langchain_exaone"]
|
|
32
|
+
|
|
33
|
+
[tool.pytest.ini_options]
|
|
34
|
+
asyncio_mode = "auto"
|
|
35
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
36
|
+
testpaths = ["tests"]
|
|
37
|
+
|
|
38
|
+
[tool.ruff]
|
|
39
|
+
line-length = 120
|
|
40
|
+
|
|
41
|
+
[tool.ruff.lint]
|
|
42
|
+
select = ["E", "F", "I", "B"]
|