langchain-dev-utils 0.1.0__py3-none-any.whl
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_dev_utils/__init__.py +24 -0
- langchain_dev_utils/chat_model.py +136 -0
- langchain_dev_utils/content.py +121 -0
- langchain_dev_utils/embbedings.py +121 -0
- langchain_dev_utils/has_tool_calling.py +19 -0
- langchain_dev_utils/py.typed +0 -0
- langchain_dev_utils-0.1.0.dist-info/METADATA +163 -0
- langchain_dev_utils-0.1.0.dist-info/RECORD +9 -0
- langchain_dev_utils-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from .has_tool_calling import has_tool_calling
|
|
2
|
+
from .content import (
|
|
3
|
+
convert_reasoning_content_for_ai_message,
|
|
4
|
+
convert_reasoning_content_for_chunk_iterator,
|
|
5
|
+
aconvert_reasoning_content_for_ai_message,
|
|
6
|
+
aconvert_reasoning_content_for_chunk_iterator,
|
|
7
|
+
)
|
|
8
|
+
from .embbedings import load_embeddings, register_embeddings_provider
|
|
9
|
+
from .chat_model import load_chat_model, register_model_provider
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"has_tool_calling",
|
|
13
|
+
"convert_reasoning_content_for_ai_message",
|
|
14
|
+
"convert_reasoning_content_for_chunk_iterator",
|
|
15
|
+
"aconvert_reasoning_content_for_ai_message",
|
|
16
|
+
"aconvert_reasoning_content_for_chunk_iterator",
|
|
17
|
+
"load_embeddings",
|
|
18
|
+
"register_embeddings_provider",
|
|
19
|
+
"load_chat_model",
|
|
20
|
+
"register_model_provider",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any, Optional, Union, cast
|
|
3
|
+
|
|
4
|
+
from langchain.chat_models.base import (
|
|
5
|
+
BaseChatModel,
|
|
6
|
+
_SUPPORTED_PROVIDERS,
|
|
7
|
+
init_chat_model,
|
|
8
|
+
_init_chat_model_helper,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_MODEL_PROVIDERS_DICT = {}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _parse_model(model: str, model_provider: Optional[str]) -> tuple[str, str]:
|
|
16
|
+
"""Parse model string and provider.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
model: Model name string, potentially including provider prefix
|
|
20
|
+
model_provider: Optional provider name
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Tuple of (model_name, provider_name)
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
ValueError: If unable to infer model provider
|
|
27
|
+
"""
|
|
28
|
+
if (
|
|
29
|
+
not model_provider
|
|
30
|
+
and ":" in model
|
|
31
|
+
and model.split(":")[0] in _MODEL_PROVIDERS_DICT
|
|
32
|
+
):
|
|
33
|
+
model_provider = model.split(":")[0]
|
|
34
|
+
model = ":".join(model.split(":")[1:])
|
|
35
|
+
if not model_provider:
|
|
36
|
+
msg = (
|
|
37
|
+
f"Unable to infer model provider for {model=}, please specify "
|
|
38
|
+
f"model_provider directly."
|
|
39
|
+
)
|
|
40
|
+
raise ValueError(msg)
|
|
41
|
+
model_provider = model_provider.replace("-", "_").lower()
|
|
42
|
+
return model, model_provider
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _load_chat_model_helper(
|
|
46
|
+
model: str,
|
|
47
|
+
model_provider: Optional[str] = None,
|
|
48
|
+
**kwargs: Any,
|
|
49
|
+
) -> BaseChatModel:
|
|
50
|
+
"""Helper function to load chat model.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
model: Model name
|
|
54
|
+
model_provider: Optional provider name
|
|
55
|
+
**kwargs: Additional arguments for model initialization
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
BaseChatModel: Initialized chat model instance
|
|
59
|
+
"""
|
|
60
|
+
model, model_provider = _parse_model(model, model_provider)
|
|
61
|
+
if model_provider in _MODEL_PROVIDERS_DICT.keys():
|
|
62
|
+
chat_model = _MODEL_PROVIDERS_DICT[model_provider]["chat_model"]
|
|
63
|
+
if isinstance(chat_model, str):
|
|
64
|
+
if not (api_key := kwargs.get("api_key")):
|
|
65
|
+
api_key = os.getenv(f"{model_provider.upper()}_API_KEY")
|
|
66
|
+
if not api_key:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"API key for {model_provider} not found. Please set it in the environment."
|
|
69
|
+
)
|
|
70
|
+
kwargs["api_key"] = api_key
|
|
71
|
+
base_url = _MODEL_PROVIDERS_DICT[model_provider]["base_url"]
|
|
72
|
+
return init_chat_model(
|
|
73
|
+
model=model,
|
|
74
|
+
model_provider=chat_model,
|
|
75
|
+
base_url=base_url,
|
|
76
|
+
**kwargs,
|
|
77
|
+
)
|
|
78
|
+
else:
|
|
79
|
+
return chat_model(model=model, **kwargs)
|
|
80
|
+
|
|
81
|
+
return _init_chat_model_helper(model, model_provider=model_provider, **kwargs)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def register_model_provider(
|
|
85
|
+
provider_name: str,
|
|
86
|
+
chat_model: Union[type[BaseChatModel], str],
|
|
87
|
+
base_url: Optional[str] = None,
|
|
88
|
+
):
|
|
89
|
+
"""Register a new model provider.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
provider_name: Name of the provider to register
|
|
93
|
+
chat_model: Either a BaseChatModel class or a string identifier for a supported provider
|
|
94
|
+
base_url: Optional base URL for API endpoints (required when chat_model is a string)
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
ValueError: If base_url is not provided when chat_model is a string,
|
|
98
|
+
or if chat_model string is not in supported providers
|
|
99
|
+
"""
|
|
100
|
+
if isinstance(chat_model, str):
|
|
101
|
+
if base_url is None:
|
|
102
|
+
raise ValueError("base_url must be provided when chat_model is a string")
|
|
103
|
+
|
|
104
|
+
if chat_model not in _SUPPORTED_PROVIDERS:
|
|
105
|
+
raise ValueError(
|
|
106
|
+
f"when chat_model is a string, the value must be one of {_SUPPORTED_PROVIDERS}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
_MODEL_PROVIDERS_DICT.update(
|
|
110
|
+
{provider_name: {"chat_model": chat_model, "base_url": base_url}}
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
_MODEL_PROVIDERS_DICT.update({provider_name: {"chat_model": chat_model}})
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def load_chat_model(
|
|
117
|
+
model: Optional[str] = None,
|
|
118
|
+
*,
|
|
119
|
+
model_provider: Optional[str] = None,
|
|
120
|
+
**kwargs: Any,
|
|
121
|
+
) -> BaseChatModel:
|
|
122
|
+
"""Load a chat model.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
model: Model name
|
|
126
|
+
model_provider: Optional provider name
|
|
127
|
+
**kwargs: Additional arguments for model initialization
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
BaseChatModel: Initialized chat model instance
|
|
131
|
+
"""
|
|
132
|
+
return _load_chat_model_helper(
|
|
133
|
+
cast(str, model),
|
|
134
|
+
model_provider=model_provider,
|
|
135
|
+
**kwargs,
|
|
136
|
+
)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from typing import AsyncIterator, Iterator, Tuple
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import (
|
|
4
|
+
AIMessage,
|
|
5
|
+
AIMessageChunk,
|
|
6
|
+
BaseMessageChunk,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def convert_reasoning_content_for_ai_message(
|
|
11
|
+
model_response: AIMessage,
|
|
12
|
+
think_tag: Tuple[str, str] = ("", ""),
|
|
13
|
+
) -> AIMessage:
|
|
14
|
+
"""Convert reasoning content in AI message to visible content.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
model_response: AI message response from model
|
|
18
|
+
think_tag: Tuple of (opening_tag, closing_tag) to wrap reasoning content
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
AIMessage: Modified AI message with reasoning content in visible content
|
|
22
|
+
"""
|
|
23
|
+
if "reasoning_content" in model_response.additional_kwargs:
|
|
24
|
+
model_response.content = f"{think_tag[0]}{model_response.additional_kwargs['reasoning_content']}{think_tag[1]}"
|
|
25
|
+
return model_response
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def convert_reasoning_content_for_chunk_iterator(
|
|
29
|
+
model_response: Iterator[BaseMessageChunk],
|
|
30
|
+
think_tag: Tuple[str, str] = ("", ""),
|
|
31
|
+
) -> Iterator[BaseMessageChunk]:
|
|
32
|
+
"""Convert reasoning content for streaming response chunks.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
model_response: Iterator of message chunks from streaming response
|
|
36
|
+
think_tag: Tuple of (opening_tag, closing_tag) to wrap reasoning content
|
|
37
|
+
|
|
38
|
+
Yields:
|
|
39
|
+
BaseMessageChunk: Modified message chunks with reasoning content
|
|
40
|
+
"""
|
|
41
|
+
isfirst = True
|
|
42
|
+
isend = True
|
|
43
|
+
|
|
44
|
+
for chunk in model_response:
|
|
45
|
+
if (
|
|
46
|
+
isinstance(chunk, AIMessageChunk)
|
|
47
|
+
and "reasoning_content" in chunk.additional_kwargs
|
|
48
|
+
):
|
|
49
|
+
if isfirst:
|
|
50
|
+
chunk.content = (
|
|
51
|
+
f"{think_tag[0]}{chunk.additional_kwargs['reasoning_content']}"
|
|
52
|
+
)
|
|
53
|
+
isfirst = False
|
|
54
|
+
else:
|
|
55
|
+
chunk.content = chunk.additional_kwargs["reasoning_content"]
|
|
56
|
+
elif (
|
|
57
|
+
isinstance(chunk, AIMessageChunk)
|
|
58
|
+
and "reasoning_content" not in chunk.additional_kwargs
|
|
59
|
+
and chunk.content
|
|
60
|
+
and isend
|
|
61
|
+
):
|
|
62
|
+
chunk.content = f"{think_tag[1]}{chunk.content}"
|
|
63
|
+
isend = False
|
|
64
|
+
yield chunk
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def aconvert_reasoning_content_for_ai_message(
|
|
68
|
+
model_response: AIMessage,
|
|
69
|
+
think_tag: Tuple[str, str] = ("", ""),
|
|
70
|
+
) -> AIMessage:
|
|
71
|
+
"""Async convert reasoning content in AI message to visible content.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
model_response: AI message response from model
|
|
75
|
+
think_tag: Tuple of (opening_tag, closing_tag) to wrap reasoning content
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
AIMessage: Modified AI message with reasoning content in visible content
|
|
79
|
+
"""
|
|
80
|
+
if "reasoning_content" in model_response.additional_kwargs:
|
|
81
|
+
model_response.content = f"{think_tag[0]}{model_response.additional_kwargs['reasoning_content']}{think_tag[1]}"
|
|
82
|
+
return model_response
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
async def aconvert_reasoning_content_for_chunk_iterator(
|
|
86
|
+
amodel_response: AsyncIterator[BaseMessageChunk],
|
|
87
|
+
think_tag: Tuple[str, str] = ("", ""),
|
|
88
|
+
) -> AsyncIterator[BaseMessageChunk]:
|
|
89
|
+
"""Async convert reasoning content for streaming response chunks.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
amodel_response: Async iterator of message chunks from streaming response
|
|
93
|
+
think_tag: Tuple of (opening_tag, closing_tag) to wrap reasoning content
|
|
94
|
+
|
|
95
|
+
Yields:
|
|
96
|
+
BaseMessageChunk: Modified message chunks with reasoning content
|
|
97
|
+
"""
|
|
98
|
+
isfirst = True
|
|
99
|
+
isend = True
|
|
100
|
+
|
|
101
|
+
async for chunk in amodel_response:
|
|
102
|
+
if (
|
|
103
|
+
isinstance(chunk, AIMessageChunk)
|
|
104
|
+
and "reasoning_content" in chunk.additional_kwargs
|
|
105
|
+
):
|
|
106
|
+
if isfirst:
|
|
107
|
+
chunk.content = (
|
|
108
|
+
f"{think_tag[0]}{chunk.additional_kwargs['reasoning_content']}"
|
|
109
|
+
)
|
|
110
|
+
isfirst = False
|
|
111
|
+
else:
|
|
112
|
+
chunk.content = chunk.additional_kwargs["reasoning_content"]
|
|
113
|
+
elif (
|
|
114
|
+
isinstance(chunk, AIMessageChunk)
|
|
115
|
+
and "reasoning_content" not in chunk.additional_kwargs
|
|
116
|
+
and chunk.content
|
|
117
|
+
and isend
|
|
118
|
+
):
|
|
119
|
+
chunk.content = f"{think_tag[1]}{chunk.content}"
|
|
120
|
+
isend = False
|
|
121
|
+
yield chunk
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any, Optional, Union
|
|
3
|
+
from langchain.embeddings.base import init_embeddings, _SUPPORTED_PROVIDERS, Embeddings
|
|
4
|
+
from langchain_core.runnables import Runnable
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_EMBEDDINGS_PROVIDERS_DICT = {}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _parse_model_string(model_name: str) -> tuple[str, str]:
|
|
11
|
+
"""Parse model string into provider and model name.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
model_name: Model name string in format 'provider:model-name'
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Tuple of (provider, model) parsed from the model_name
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
ValueError: If model name format is invalid or model name is empty
|
|
21
|
+
"""
|
|
22
|
+
if ":" not in model_name:
|
|
23
|
+
msg = (
|
|
24
|
+
f"Invalid model format '{model_name}'.\n"
|
|
25
|
+
f"Model name must be in format 'provider:model-name'\n"
|
|
26
|
+
)
|
|
27
|
+
raise ValueError(msg)
|
|
28
|
+
|
|
29
|
+
provider, model = model_name.split(":", 1)
|
|
30
|
+
provider = provider.lower().strip()
|
|
31
|
+
model = model.strip()
|
|
32
|
+
if not model:
|
|
33
|
+
msg = "Model name cannot be empty"
|
|
34
|
+
raise ValueError(msg)
|
|
35
|
+
return provider, model
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def register_embeddings_provider(
|
|
39
|
+
provider_name: str,
|
|
40
|
+
embeddings_model: Union[type[Embeddings], str],
|
|
41
|
+
base_url: Optional[str] = None,
|
|
42
|
+
):
|
|
43
|
+
"""Register an embeddings provider.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
provider_name: Name of the provider to register
|
|
47
|
+
embeddings_model: Either an Embeddings class or a string identifier for a supported provider
|
|
48
|
+
base_url: Optional base URL for API endpoints (required when embeddings_model is a string)
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
ValueError: If base_url is not provided when embeddings_model is a string
|
|
52
|
+
"""
|
|
53
|
+
if isinstance(embeddings_model, str):
|
|
54
|
+
if base_url is None:
|
|
55
|
+
raise ValueError(
|
|
56
|
+
"base_url must be provided when embeddings_model is a string"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if embeddings_model not in _SUPPORTED_PROVIDERS:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"when embeddings_model is a string, the value must be one of {_SUPPORTED_PROVIDERS}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
_EMBEDDINGS_PROVIDERS_DICT.update(
|
|
65
|
+
{
|
|
66
|
+
provider_name: {
|
|
67
|
+
"embeddings_model": embeddings_model,
|
|
68
|
+
"base_url": base_url,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
_EMBEDDINGS_PROVIDERS_DICT.update(
|
|
74
|
+
{provider_name: {"embeddings_model": embeddings_model}}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def load_embeddings(
|
|
79
|
+
model: str,
|
|
80
|
+
*,
|
|
81
|
+
provider: Optional[str] = None,
|
|
82
|
+
**kwargs: Any,
|
|
83
|
+
) -> Union[Embeddings, Runnable[Any, list[float]]]:
|
|
84
|
+
"""Load embeddings model.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
model: Model name in format 'provider:model-name' if provider not specified separately
|
|
88
|
+
provider: Optional provider name (if not included in model parameter)
|
|
89
|
+
**kwargs: Additional arguments for model initialization
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Union[Embeddings, Runnable[Any, list[float]]]: Initialized embeddings model instance
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
ValueError: If provider is not registered or API key is not found
|
|
96
|
+
"""
|
|
97
|
+
if provider is None:
|
|
98
|
+
provider, model = _parse_model_string(model)
|
|
99
|
+
if provider not in _EMBEDDINGS_PROVIDERS_DICT:
|
|
100
|
+
raise ValueError(f"Provider {provider} not registered")
|
|
101
|
+
|
|
102
|
+
embeddings = _EMBEDDINGS_PROVIDERS_DICT[provider]["embeddings_model"]
|
|
103
|
+
if isinstance(embeddings, str):
|
|
104
|
+
if not (api_key := kwargs.get("api_key")):
|
|
105
|
+
api_key = os.getenv(f"{provider.upper()}_API_KEY")
|
|
106
|
+
if not api_key:
|
|
107
|
+
raise ValueError(
|
|
108
|
+
f"API key for {provider} not found. Please set it in the environment."
|
|
109
|
+
)
|
|
110
|
+
kwargs["api_key"] = api_key
|
|
111
|
+
if embeddings == "openai":
|
|
112
|
+
kwargs["check_embedding_ctx_length"] = False
|
|
113
|
+
|
|
114
|
+
return init_embeddings(
|
|
115
|
+
model=model,
|
|
116
|
+
provider=embeddings,
|
|
117
|
+
base_url=_EMBEDDINGS_PROVIDERS_DICT[provider]["base_url"],
|
|
118
|
+
**kwargs,
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
return embeddings(model=model, **kwargs)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from langchain_core.messages import AIMessage, AnyMessage
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def has_tool_calling(message: AnyMessage):
|
|
5
|
+
"""Check if a message contains tool calls.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
message: Any message type to check for tool calls
|
|
9
|
+
|
|
10
|
+
Returns:
|
|
11
|
+
bool: True if message is an AIMessage with tool calls, False otherwise
|
|
12
|
+
"""
|
|
13
|
+
if (
|
|
14
|
+
isinstance(message, AIMessage)
|
|
15
|
+
and hasattr(message, "tool_calls")
|
|
16
|
+
and len(message.tool_calls) > 0
|
|
17
|
+
):
|
|
18
|
+
return True
|
|
19
|
+
return False
|
|
File without changes
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: langchain-dev-utils
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A practical utility library for LangChain and LangGraph development
|
|
5
|
+
Project-URL: Source Code, https://github.com/TBice123123/langchain-dev-utils
|
|
6
|
+
Project-URL: repository, https://github.com/TBice123123/langchain-dev-utils
|
|
7
|
+
Author-email: tiebingice <tiebingice123@outlook.com>
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Requires-Dist: langchain>=0.3.27
|
|
10
|
+
Requires-Dist: langgraph>=0.6.6
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# LangChain Dev Utils
|
|
14
|
+
|
|
15
|
+
[中文文档](https://github.com/TBice123123/langchain-dev-utils/blob/master/README_cn.md)
|
|
16
|
+
|
|
17
|
+
This toolkit is designed to provide encapsulated utility tools for developers using LangChain and LangGraph to develop large language model applications, helping developers work more efficiently.
|
|
18
|
+
|
|
19
|
+
## Installation and Usage
|
|
20
|
+
|
|
21
|
+
1. Using pip
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install -U langchain-dev-utils
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
2. Using poetry
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
poetry add langchain-dev-utils
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
3. Using uv
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv add langchain-dev-utils
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Function Modules
|
|
40
|
+
|
|
41
|
+
### 1. Extended Model Loading Functionality
|
|
42
|
+
|
|
43
|
+
While the official `init_chat_model` function is very useful, it has limited support for model providers. This toolkit provides extended model loading functionality that allows registration and use of more model providers.
|
|
44
|
+
|
|
45
|
+
#### Core Functions
|
|
46
|
+
|
|
47
|
+
- `register_model_provider`: Register a model provider
|
|
48
|
+
- `load_chat_model`: Load a chat model
|
|
49
|
+
|
|
50
|
+
#### `register_model_provider` Parameter Description
|
|
51
|
+
|
|
52
|
+
- `provider_name`: Provider name, requires a custom name
|
|
53
|
+
- `chat_model`: ChatModel class or string. If it's a string, it must be a provider supported by the official `init_chat_model` (e.g., `openai`, `anthropic`). In this case, the `init_chat_model` function will be called
|
|
54
|
+
- `base_url`: Optional base URL. Recommended when `chat_model` is a string
|
|
55
|
+
|
|
56
|
+
#### Usage Example
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from langchain_dev_utils.chat_model import register_model_provider, load_chat_model
|
|
60
|
+
from langchain_qwq import ChatQwen
|
|
61
|
+
from dotenv import load_dotenv
|
|
62
|
+
|
|
63
|
+
load_dotenv()
|
|
64
|
+
|
|
65
|
+
# Register custom model providers
|
|
66
|
+
register_model_provider("dashscope", ChatQwen)
|
|
67
|
+
register_model_provider("openrouter", "openai", base_url="https://openrouter.ai/api/v1")
|
|
68
|
+
|
|
69
|
+
# Load models
|
|
70
|
+
model = load_chat_model(model="dashscope:qwen-flash")
|
|
71
|
+
print(model.invoke("Hello!"))
|
|
72
|
+
|
|
73
|
+
model = load_chat_model(model="openrouter:moonshotai/kimi-k2-0905")
|
|
74
|
+
print(model.invoke("Hello!"))
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Note**: Since the underlying implementation of the function is a global dictionary, **all model providers must be registered at application startup**. Modifications should not be made at runtime, otherwise multi-threading concurrency synchronization issues may occur.
|
|
78
|
+
|
|
79
|
+
### 2. Reasoning Content Processing Functionality
|
|
80
|
+
|
|
81
|
+
Provides utility functions for processing model reasoning content, supporting both synchronous and asynchronous operations.
|
|
82
|
+
|
|
83
|
+
#### Core Functions
|
|
84
|
+
|
|
85
|
+
- `convert_reasoning_content_for_ai_message`: Convert reasoning content for a single AI message
|
|
86
|
+
- `convert_reasoning_content_for_chunk_iterator`: Convert reasoning content for streaming response message chunk iterator
|
|
87
|
+
- `aconvert_reasoning_content_for_ai_message`: Asynchronously convert reasoning content for a single AI message
|
|
88
|
+
- `aconvert_reasoning_content_for_chunk_iterator`: Asynchronously convert reasoning content for streaming response message chunk iterator
|
|
89
|
+
|
|
90
|
+
#### Usage Example
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
# Synchronously process reasoning content
|
|
94
|
+
from langchain_dev_utils.content import convert_reasoning_content_for_ai_message
|
|
95
|
+
|
|
96
|
+
response = model.invoke("Please solve this math problem")
|
|
97
|
+
converted_response = convert_reasoning_content_for_ai_message(response, think_tag=("", ""))
|
|
98
|
+
|
|
99
|
+
# Stream processing reasoning content
|
|
100
|
+
from langchain_dev_utils.content import convert_reasoning_content_for_chunk_iterator
|
|
101
|
+
|
|
102
|
+
for chunk in convert_reasoning_content_for_chunk_iterator(model.stream("Please solve this math problem"), think_tag=("", "")):
|
|
103
|
+
print(chunk.content, end="", flush=True)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 3. Embeddings Model Loading Functionality
|
|
107
|
+
|
|
108
|
+
Provides extended embeddings model loading functionality, similar to the model loading functionality.
|
|
109
|
+
|
|
110
|
+
#### Core Functions
|
|
111
|
+
|
|
112
|
+
- `register_embeddings_provider`: Register an embeddings model provider
|
|
113
|
+
- `load_embeddings`: Load an embeddings model
|
|
114
|
+
|
|
115
|
+
#### Usage Example
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from langchain_dev_utils.embbedings import register_embeddings_provider, load_embeddings
|
|
119
|
+
|
|
120
|
+
# Register embeddings model provider
|
|
121
|
+
register_embeddings_provider("openai", "openai", base_url="https://api.openai.com/v1")
|
|
122
|
+
|
|
123
|
+
# Load embeddings model
|
|
124
|
+
embeddings = load_embeddings("openai:text-embedding-ada-002")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 4. Tool Calling Detection Functionality
|
|
128
|
+
|
|
129
|
+
Provides a simple function to detect whether a message contains tool calls.
|
|
130
|
+
|
|
131
|
+
#### Core Functions
|
|
132
|
+
|
|
133
|
+
- `has_tool_calling`: Detect whether a message contains tool calls
|
|
134
|
+
|
|
135
|
+
#### Usage Example
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from langchain_dev_utils.has_tool_calling import has_tool_calling
|
|
139
|
+
|
|
140
|
+
if has_tool_calling(message):
|
|
141
|
+
# Handle tool calling logic
|
|
142
|
+
pass
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Test
|
|
146
|
+
|
|
147
|
+
All the current tool functions in this project have been tested, and you can also clone this project for testing.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
git clone https://github.com/TBice123123/langchain-dev-utils.git
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
cd langchain-dev-utils
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
uv sync --group test
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
uv run pytest .
|
|
163
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
langchain_dev_utils/__init__.py,sha256=v9MXwLR7iSJcZNV9rl7HfbqkMztYBfCoabT-hNiGdAE,779
|
|
2
|
+
langchain_dev_utils/chat_model.py,sha256=HeihWUOJS1iRcjMTshbVVWeUcdjuHmfjG5aqL85SWh4,4402
|
|
3
|
+
langchain_dev_utils/content.py,sha256=2FdyiFiqtEmCmIJ5n9EtxF-Z9cTj5cT7DPPxkU1CPiQ,4274
|
|
4
|
+
langchain_dev_utils/embbedings.py,sha256=frq6WxYcPRRVTvfTHBqqu6xLmM2_03emmyDrkZxoDkE,4138
|
|
5
|
+
langchain_dev_utils/has_tool_calling.py,sha256=w3JP7uc5khmAjBTH63rGXUWh6jrMX1ikg9FmEt8wdOc,524
|
|
6
|
+
langchain_dev_utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
langchain_dev_utils-0.1.0.dist-info/METADATA,sha256=yy1zkuW2SEI2cpfCuXEyNsIYA4m1oxyMc3dGZEQmf9w,5225
|
|
8
|
+
langchain_dev_utils-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
9
|
+
langchain_dev_utils-0.1.0.dist-info/RECORD,,
|