ollama-haystack 2.1.2__tar.gz → 2.2.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.
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/CHANGELOG.md +7 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/PKG-INFO +1 -1
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/pyproject.toml +1 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/src/haystack_integrations/components/generators/ollama/chat/chat_generator.py +104 -39
- ollama_haystack-2.2.0/tests/test_chat_generator.py +458 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/tests/test_generator.py +2 -2
- ollama_haystack-2.1.2/tests/test_chat_generator.py +0 -169
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/.gitignore +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/LICENSE.txt +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/README.md +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/examples/chat_generator_example.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/examples/embedders_example.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/examples/generator_example.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/pydoc/config.yml +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/src/haystack_integrations/components/embedders/ollama/__init__.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/src/haystack_integrations/components/embedders/ollama/document_embedder.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/src/haystack_integrations/components/embedders/ollama/text_embedder.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/src/haystack_integrations/components/generators/ollama/__init__.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/src/haystack_integrations/components/generators/ollama/chat/__init__.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/src/haystack_integrations/components/generators/ollama/generator.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/tests/__init__.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/tests/test_document_embedder.py +0 -0
- {ollama_haystack-2.1.2 → ollama_haystack-2.2.0}/tests/test_text_embedder.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ollama-haystack
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: An integration between the Ollama LLM framework and Haystack
|
|
5
5
|
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/ollama#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
@@ -1,12 +1,68 @@
|
|
|
1
1
|
from typing import Any, Callable, Dict, List, Optional, Union
|
|
2
2
|
|
|
3
3
|
from haystack import component, default_from_dict, default_to_dict
|
|
4
|
-
from haystack.dataclasses import ChatMessage, StreamingChunk
|
|
4
|
+
from haystack.dataclasses import ChatMessage, StreamingChunk, ToolCall
|
|
5
|
+
from haystack.tools import Tool, _check_duplicate_tool_names, deserialize_tools_inplace
|
|
5
6
|
from haystack.utils.callable_serialization import deserialize_callable, serialize_callable
|
|
6
7
|
|
|
7
8
|
from ollama import ChatResponse, Client
|
|
8
9
|
|
|
9
10
|
|
|
11
|
+
def _convert_chatmessage_to_ollama_format(message: ChatMessage) -> Dict[str, Any]:
|
|
12
|
+
"""
|
|
13
|
+
Convert a ChatMessage to the format expected by Ollama Chat API.
|
|
14
|
+
"""
|
|
15
|
+
text_contents = message.texts
|
|
16
|
+
tool_calls = message.tool_calls
|
|
17
|
+
tool_call_results = message.tool_call_results
|
|
18
|
+
|
|
19
|
+
if not text_contents and not tool_calls and not tool_call_results:
|
|
20
|
+
msg = "A `ChatMessage` must contain at least one `TextContent`, `ToolCall`, or `ToolCallResult`."
|
|
21
|
+
raise ValueError(msg)
|
|
22
|
+
elif len(text_contents) + len(tool_call_results) > 1:
|
|
23
|
+
msg = "A `ChatMessage` can only contain one `TextContent` or one `ToolCallResult`."
|
|
24
|
+
raise ValueError(msg)
|
|
25
|
+
|
|
26
|
+
ollama_msg: Dict[str, Any] = {"role": message._role.value}
|
|
27
|
+
|
|
28
|
+
if tool_call_results:
|
|
29
|
+
# Ollama does not provide a way to communicate errors in tool invocations, so we ignore the error field
|
|
30
|
+
ollama_msg["content"] = tool_call_results[0].result
|
|
31
|
+
return ollama_msg
|
|
32
|
+
|
|
33
|
+
if text_contents:
|
|
34
|
+
ollama_msg["content"] = text_contents[0]
|
|
35
|
+
if tool_calls:
|
|
36
|
+
# Ollama does not support tool call id, so we ignore it
|
|
37
|
+
ollama_msg["tool_calls"] = [
|
|
38
|
+
{"type": "function", "function": {"name": tc.tool_name, "arguments": tc.arguments}} for tc in tool_calls
|
|
39
|
+
]
|
|
40
|
+
return ollama_msg
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _convert_ollama_response_to_chatmessage(ollama_response: "ChatResponse") -> ChatMessage:
|
|
44
|
+
"""
|
|
45
|
+
Converts the non-streaming response from the Ollama API to a ChatMessage with assistant role.
|
|
46
|
+
"""
|
|
47
|
+
response_dict = ollama_response.model_dump()
|
|
48
|
+
|
|
49
|
+
ollama_message = response_dict["message"]
|
|
50
|
+
|
|
51
|
+
text = ollama_message["content"]
|
|
52
|
+
|
|
53
|
+
tool_calls = []
|
|
54
|
+
if ollama_tool_calls := ollama_message.get("tool_calls"):
|
|
55
|
+
for ollama_tc in ollama_tool_calls:
|
|
56
|
+
tool_calls.append(
|
|
57
|
+
ToolCall(tool_name=ollama_tc["function"]["name"], arguments=ollama_tc["function"]["arguments"])
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
message = ChatMessage.from_assistant(text=text, tool_calls=tool_calls)
|
|
61
|
+
|
|
62
|
+
message.meta.update({key: value for key, value in response_dict.items() if key != "message"})
|
|
63
|
+
return message
|
|
64
|
+
|
|
65
|
+
|
|
10
66
|
@component
|
|
11
67
|
class OllamaChatGenerator:
|
|
12
68
|
"""
|
|
@@ -40,6 +96,7 @@ class OllamaChatGenerator:
|
|
|
40
96
|
timeout: int = 120,
|
|
41
97
|
keep_alive: Optional[Union[float, str]] = None,
|
|
42
98
|
streaming_callback: Optional[Callable[[StreamingChunk], None]] = None,
|
|
99
|
+
tools: Optional[List[Tool]] = None,
|
|
43
100
|
):
|
|
44
101
|
"""
|
|
45
102
|
:param model:
|
|
@@ -52,9 +109,6 @@ class OllamaChatGenerator:
|
|
|
52
109
|
[Ollama docs](https://github.com/jmorganca/ollama/blob/main/docs/modelfile.md#valid-parameters-and-values).
|
|
53
110
|
:param timeout:
|
|
54
111
|
The number of seconds before throwing a timeout error from the Ollama API.
|
|
55
|
-
:param streaming_callback:
|
|
56
|
-
A callback function that is called when a new token is received from the stream.
|
|
57
|
-
The callback function accepts StreamingChunk as an argument.
|
|
58
112
|
:param keep_alive:
|
|
59
113
|
The option that controls how long the model will stay loaded into memory following the request.
|
|
60
114
|
If not set, it will use the default value from the Ollama (5 minutes).
|
|
@@ -63,14 +117,24 @@ class OllamaChatGenerator:
|
|
|
63
117
|
- a number in seconds (such as 3600)
|
|
64
118
|
- any negative number which will keep the model loaded in memory (e.g. -1 or "-1m")
|
|
65
119
|
- '0' which will unload the model immediately after generating a response.
|
|
120
|
+
:param streaming_callback:
|
|
121
|
+
A callback function that is called when a new token is received from the stream.
|
|
122
|
+
The callback function accepts StreamingChunk as an argument.
|
|
123
|
+
:param tools:
|
|
124
|
+
A list of tools for which the model can prepare calls.
|
|
125
|
+
Not all models support tools. For a list of models compatible with tools, see the
|
|
126
|
+
[models page](https://ollama.com/search?c=tools).
|
|
66
127
|
"""
|
|
67
128
|
|
|
129
|
+
_check_duplicate_tool_names(tools)
|
|
130
|
+
|
|
68
131
|
self.timeout = timeout
|
|
69
132
|
self.generation_kwargs = generation_kwargs or {}
|
|
70
133
|
self.url = url
|
|
71
134
|
self.model = model
|
|
72
135
|
self.keep_alive = keep_alive
|
|
73
136
|
self.streaming_callback = streaming_callback
|
|
137
|
+
self.tools = tools
|
|
74
138
|
|
|
75
139
|
self._client = Client(host=self.url, timeout=self.timeout)
|
|
76
140
|
|
|
@@ -82,6 +146,7 @@ class OllamaChatGenerator:
|
|
|
82
146
|
Dictionary with serialized data.
|
|
83
147
|
"""
|
|
84
148
|
callback_name = serialize_callable(self.streaming_callback) if self.streaming_callback else None
|
|
149
|
+
serialized_tools = [tool.to_dict() for tool in self.tools] if self.tools else None
|
|
85
150
|
return default_to_dict(
|
|
86
151
|
self,
|
|
87
152
|
model=self.model,
|
|
@@ -90,6 +155,7 @@ class OllamaChatGenerator:
|
|
|
90
155
|
generation_kwargs=self.generation_kwargs,
|
|
91
156
|
timeout=self.timeout,
|
|
92
157
|
streaming_callback=callback_name,
|
|
158
|
+
tools=serialized_tools,
|
|
93
159
|
)
|
|
94
160
|
|
|
95
161
|
@classmethod
|
|
@@ -102,34 +168,14 @@ class OllamaChatGenerator:
|
|
|
102
168
|
:returns:
|
|
103
169
|
Deserialized component.
|
|
104
170
|
"""
|
|
171
|
+
deserialize_tools_inplace(data["init_parameters"], key="tools")
|
|
172
|
+
|
|
105
173
|
init_params = data.get("init_parameters", {})
|
|
106
|
-
|
|
107
|
-
if serialized_callback_handler:
|
|
174
|
+
|
|
175
|
+
if serialized_callback_handler := init_params.get("streaming_callback"):
|
|
108
176
|
data["init_parameters"]["streaming_callback"] = deserialize_callable(serialized_callback_handler)
|
|
109
177
|
return default_from_dict(cls, data)
|
|
110
178
|
|
|
111
|
-
def _message_to_dict(self, message: ChatMessage) -> Dict[str, str]:
|
|
112
|
-
return {"role": message.role.value, "content": message.text}
|
|
113
|
-
|
|
114
|
-
def _build_message_from_ollama_response(self, ollama_response: ChatResponse) -> ChatMessage:
|
|
115
|
-
"""
|
|
116
|
-
Converts the non-streaming response from the Ollama API to a ChatMessage.
|
|
117
|
-
"""
|
|
118
|
-
response_dict = ollama_response.model_dump()
|
|
119
|
-
message = ChatMessage.from_assistant(response_dict["message"]["content"])
|
|
120
|
-
message.meta.update({key: value for key, value in response_dict.items() if key != "message"})
|
|
121
|
-
return message
|
|
122
|
-
|
|
123
|
-
def _convert_to_streaming_response(self, chunks: List[StreamingChunk]) -> Dict[str, List[Any]]:
|
|
124
|
-
"""
|
|
125
|
-
Converts a list of chunks response required Haystack format.
|
|
126
|
-
"""
|
|
127
|
-
|
|
128
|
-
replies = [ChatMessage.from_assistant("".join([c.content for c in chunks]))]
|
|
129
|
-
meta = {key: value for key, value in chunks[0].meta.items() if key != "message"}
|
|
130
|
-
|
|
131
|
-
return {"replies": replies, "meta": [meta]}
|
|
132
|
-
|
|
133
179
|
def _build_chunk(self, chunk_response: Any) -> StreamingChunk:
|
|
134
180
|
"""
|
|
135
181
|
Converts the response from the Ollama API to a StreamingChunk.
|
|
@@ -143,23 +189,28 @@ class OllamaChatGenerator:
|
|
|
143
189
|
chunk_message = StreamingChunk(content, meta)
|
|
144
190
|
return chunk_message
|
|
145
191
|
|
|
146
|
-
def _handle_streaming_response(self, response) -> List[
|
|
192
|
+
def _handle_streaming_response(self, response) -> Dict[str, List[Any]]:
|
|
147
193
|
"""
|
|
148
|
-
Handles
|
|
194
|
+
Handles streaming response and converts it to Haystack format
|
|
149
195
|
"""
|
|
150
196
|
chunks: List[StreamingChunk] = []
|
|
151
197
|
for chunk in response:
|
|
152
|
-
chunk_delta
|
|
198
|
+
chunk_delta = self._build_chunk(chunk)
|
|
153
199
|
chunks.append(chunk_delta)
|
|
154
200
|
if self.streaming_callback is not None:
|
|
155
201
|
self.streaming_callback(chunk_delta)
|
|
156
|
-
|
|
202
|
+
|
|
203
|
+
replies = [ChatMessage.from_assistant("".join([c.content for c in chunks]))]
|
|
204
|
+
meta = {key: value for key, value in chunks[0].meta.items() if key != "message"}
|
|
205
|
+
|
|
206
|
+
return {"replies": replies, "meta": [meta]}
|
|
157
207
|
|
|
158
208
|
@component.output_types(replies=List[ChatMessage])
|
|
159
209
|
def run(
|
|
160
210
|
self,
|
|
161
211
|
messages: List[ChatMessage],
|
|
162
212
|
generation_kwargs: Optional[Dict[str, Any]] = None,
|
|
213
|
+
tools: Optional[List[Tool]] = None,
|
|
163
214
|
):
|
|
164
215
|
"""
|
|
165
216
|
Runs an Ollama Model on a given chat history.
|
|
@@ -170,21 +221,35 @@ class OllamaChatGenerator:
|
|
|
170
221
|
Optional arguments to pass to the Ollama generation endpoint, such as temperature,
|
|
171
222
|
top_p, etc. See the
|
|
172
223
|
[Ollama docs](https://github.com/jmorganca/ollama/blob/main/docs/modelfile.md#valid-parameters-and-values).
|
|
173
|
-
:param
|
|
174
|
-
A
|
|
224
|
+
:param tools:
|
|
225
|
+
A list of tools for which the model can prepare calls. If set, it will override the `tools` parameter set
|
|
226
|
+
during component initialization.
|
|
175
227
|
:returns: A dictionary with the following keys:
|
|
176
228
|
- `replies`: The responses from the model
|
|
177
229
|
"""
|
|
178
230
|
generation_kwargs = {**self.generation_kwargs, **(generation_kwargs or {})}
|
|
179
231
|
|
|
180
232
|
stream = self.streaming_callback is not None
|
|
181
|
-
|
|
233
|
+
tools = tools or self.tools
|
|
234
|
+
_check_duplicate_tool_names(tools)
|
|
235
|
+
|
|
236
|
+
if stream and tools:
|
|
237
|
+
msg = "Ollama does not support tools and streaming at the same time. Please choose one."
|
|
238
|
+
raise ValueError(msg)
|
|
239
|
+
|
|
240
|
+
ollama_tools = [{"type": "function", "function": {**t.tool_spec}} for t in tools] if tools else None
|
|
241
|
+
|
|
242
|
+
ollama_messages = [_convert_chatmessage_to_ollama_format(msg) for msg in messages]
|
|
182
243
|
response = self._client.chat(
|
|
183
|
-
model=self.model,
|
|
244
|
+
model=self.model,
|
|
245
|
+
messages=ollama_messages,
|
|
246
|
+
tools=ollama_tools,
|
|
247
|
+
stream=stream,
|
|
248
|
+
keep_alive=self.keep_alive,
|
|
249
|
+
options=generation_kwargs,
|
|
184
250
|
)
|
|
185
251
|
|
|
186
252
|
if stream:
|
|
187
|
-
|
|
188
|
-
return self._convert_to_streaming_response(chunks)
|
|
253
|
+
return self._handle_streaming_response(response)
|
|
189
254
|
|
|
190
|
-
return {"replies": [
|
|
255
|
+
return {"replies": [_convert_ollama_response_to_chatmessage(response)]}
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from unittest.mock import Mock, patch
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
from haystack.components.generators.utils import print_streaming_chunk
|
|
6
|
+
from haystack.dataclasses import (
|
|
7
|
+
ChatMessage,
|
|
8
|
+
ChatRole,
|
|
9
|
+
StreamingChunk,
|
|
10
|
+
TextContent,
|
|
11
|
+
ToolCall,
|
|
12
|
+
)
|
|
13
|
+
from haystack.tools import Tool
|
|
14
|
+
from ollama._types import ChatResponse, ResponseError
|
|
15
|
+
|
|
16
|
+
from haystack_integrations.components.generators.ollama.chat.chat_generator import (
|
|
17
|
+
OllamaChatGenerator,
|
|
18
|
+
_convert_chatmessage_to_ollama_format,
|
|
19
|
+
_convert_ollama_response_to_chatmessage,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def tools():
|
|
25
|
+
tool_parameters = {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": {"city": {"type": "string"}},
|
|
28
|
+
"required": ["city"],
|
|
29
|
+
}
|
|
30
|
+
tool = Tool(
|
|
31
|
+
name="weather",
|
|
32
|
+
description="useful to determine the weather in a given location",
|
|
33
|
+
parameters=tool_parameters,
|
|
34
|
+
function=lambda x: x,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return [tool]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_convert_chatmessage_to_ollama_format():
|
|
41
|
+
message = ChatMessage.from_system("You are good assistant")
|
|
42
|
+
assert _convert_chatmessage_to_ollama_format(message) == {
|
|
43
|
+
"role": "system",
|
|
44
|
+
"content": "You are good assistant",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
message = ChatMessage.from_user("I have a question")
|
|
48
|
+
assert _convert_chatmessage_to_ollama_format(message) == {
|
|
49
|
+
"role": "user",
|
|
50
|
+
"content": "I have a question",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
message = ChatMessage.from_assistant(text="I have an answer", meta={"finish_reason": "stop"})
|
|
54
|
+
assert _convert_chatmessage_to_ollama_format(message) == {
|
|
55
|
+
"role": "assistant",
|
|
56
|
+
"content": "I have an answer",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
message = ChatMessage.from_assistant(
|
|
60
|
+
tool_calls=[ToolCall(id="123", tool_name="weather", arguments={"city": "Paris"})]
|
|
61
|
+
)
|
|
62
|
+
assert _convert_chatmessage_to_ollama_format(message) == {
|
|
63
|
+
"role": "assistant",
|
|
64
|
+
"tool_calls": [
|
|
65
|
+
{
|
|
66
|
+
"type": "function",
|
|
67
|
+
"function": {"name": "weather", "arguments": {"city": "Paris"}},
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
tool_result = json.dumps({"weather": "sunny", "temperature": "25"})
|
|
73
|
+
message = ChatMessage.from_tool(
|
|
74
|
+
tool_result=tool_result,
|
|
75
|
+
origin=ToolCall(tool_name="weather", arguments={"city": "Paris"}),
|
|
76
|
+
)
|
|
77
|
+
assert _convert_chatmessage_to_ollama_format(message) == {
|
|
78
|
+
"role": "tool",
|
|
79
|
+
"content": tool_result,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_convert_chatmessage_to_ollama_invalid():
|
|
84
|
+
message = ChatMessage(_role=ChatRole.ASSISTANT, _content=[])
|
|
85
|
+
with pytest.raises(ValueError):
|
|
86
|
+
_convert_chatmessage_to_ollama_format(message)
|
|
87
|
+
|
|
88
|
+
message = ChatMessage(
|
|
89
|
+
_role=ChatRole.ASSISTANT,
|
|
90
|
+
_content=[
|
|
91
|
+
TextContent(text="I have an answer"),
|
|
92
|
+
TextContent(text="I have another answer"),
|
|
93
|
+
],
|
|
94
|
+
)
|
|
95
|
+
with pytest.raises(ValueError):
|
|
96
|
+
_convert_chatmessage_to_ollama_format(message)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_convert_ollama_response_to_chatmessage():
|
|
100
|
+
model = "some_model"
|
|
101
|
+
|
|
102
|
+
ollama_response = ChatResponse(
|
|
103
|
+
model=model,
|
|
104
|
+
created_at="2023-12-12T14:13:43.416799Z",
|
|
105
|
+
message={"role": "assistant", "content": "Hello! How are you today?"},
|
|
106
|
+
done=True,
|
|
107
|
+
total_duration=5191566416,
|
|
108
|
+
load_duration=2154458,
|
|
109
|
+
prompt_eval_count=26,
|
|
110
|
+
prompt_eval_duration=383809000,
|
|
111
|
+
eval_count=298,
|
|
112
|
+
eval_duration=4799921000,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
observed = _convert_ollama_response_to_chatmessage(ollama_response)
|
|
116
|
+
|
|
117
|
+
assert observed.role == "assistant"
|
|
118
|
+
assert observed.text == "Hello! How are you today?"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_convert_ollama_response_to_chatmessage_with_tools():
|
|
122
|
+
model = "some_model"
|
|
123
|
+
|
|
124
|
+
ollama_response = ChatResponse(
|
|
125
|
+
model=model,
|
|
126
|
+
created_at="2023-12-12T14:13:43.416799Z",
|
|
127
|
+
message={
|
|
128
|
+
"role": "assistant",
|
|
129
|
+
"content": "",
|
|
130
|
+
"tool_calls": [
|
|
131
|
+
{
|
|
132
|
+
"function": {
|
|
133
|
+
"name": "get_current_weather",
|
|
134
|
+
"arguments": {"format": "celsius", "location": "Paris, FR"},
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
done=True,
|
|
140
|
+
total_duration=5191566416,
|
|
141
|
+
load_duration=2154458,
|
|
142
|
+
prompt_eval_count=26,
|
|
143
|
+
prompt_eval_duration=383809000,
|
|
144
|
+
eval_count=298,
|
|
145
|
+
eval_duration=4799921000,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
observed = _convert_ollama_response_to_chatmessage(ollama_response)
|
|
149
|
+
|
|
150
|
+
assert observed.role == "assistant"
|
|
151
|
+
assert observed.text == ""
|
|
152
|
+
assert observed.tool_call == ToolCall(
|
|
153
|
+
tool_name="get_current_weather",
|
|
154
|
+
arguments={"format": "celsius", "location": "Paris, FR"},
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class TestOllamaChatGenerator:
|
|
159
|
+
def test_init_default(self):
|
|
160
|
+
component = OllamaChatGenerator()
|
|
161
|
+
assert component.model == "orca-mini"
|
|
162
|
+
assert component.url == "http://localhost:11434"
|
|
163
|
+
assert component.generation_kwargs == {}
|
|
164
|
+
assert component.timeout == 120
|
|
165
|
+
assert component.streaming_callback is None
|
|
166
|
+
assert component.tools is None
|
|
167
|
+
assert component.keep_alive is None
|
|
168
|
+
|
|
169
|
+
def test_init(self, tools):
|
|
170
|
+
component = OllamaChatGenerator(
|
|
171
|
+
model="llama2",
|
|
172
|
+
url="http://my-custom-endpoint:11434",
|
|
173
|
+
generation_kwargs={"temperature": 0.5},
|
|
174
|
+
timeout=5,
|
|
175
|
+
keep_alive="10m",
|
|
176
|
+
streaming_callback=print_streaming_chunk,
|
|
177
|
+
tools=tools,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
assert component.model == "llama2"
|
|
181
|
+
assert component.url == "http://my-custom-endpoint:11434"
|
|
182
|
+
assert component.generation_kwargs == {"temperature": 0.5}
|
|
183
|
+
assert component.timeout == 5
|
|
184
|
+
assert component.keep_alive == "10m"
|
|
185
|
+
assert component.streaming_callback is print_streaming_chunk
|
|
186
|
+
assert component.tools == tools
|
|
187
|
+
|
|
188
|
+
def test_init_fail_with_duplicate_tool_names(self, tools):
|
|
189
|
+
|
|
190
|
+
duplicate_tools = [tools[0], tools[0]]
|
|
191
|
+
with pytest.raises(ValueError):
|
|
192
|
+
OllamaChatGenerator(tools=duplicate_tools)
|
|
193
|
+
|
|
194
|
+
def test_to_dict(self):
|
|
195
|
+
tool = Tool(
|
|
196
|
+
name="name",
|
|
197
|
+
description="description",
|
|
198
|
+
parameters={"x": {"type": "string"}},
|
|
199
|
+
function=print,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
component = OllamaChatGenerator(
|
|
203
|
+
model="llama2",
|
|
204
|
+
streaming_callback=print_streaming_chunk,
|
|
205
|
+
url="custom_url",
|
|
206
|
+
generation_kwargs={"max_tokens": 10, "some_test_param": "test-params"},
|
|
207
|
+
tools=[tool],
|
|
208
|
+
keep_alive="5m",
|
|
209
|
+
)
|
|
210
|
+
data = component.to_dict()
|
|
211
|
+
assert data == {
|
|
212
|
+
"type": "haystack_integrations.components.generators.ollama.chat.chat_generator.OllamaChatGenerator",
|
|
213
|
+
"init_parameters": {
|
|
214
|
+
"timeout": 120,
|
|
215
|
+
"model": "llama2",
|
|
216
|
+
"url": "custom_url",
|
|
217
|
+
"streaming_callback": "haystack.components.generators.utils.print_streaming_chunk",
|
|
218
|
+
"keep_alive": "5m",
|
|
219
|
+
"generation_kwargs": {
|
|
220
|
+
"max_tokens": 10,
|
|
221
|
+
"some_test_param": "test-params",
|
|
222
|
+
},
|
|
223
|
+
"tools": [
|
|
224
|
+
{
|
|
225
|
+
"type": "haystack.tools.tool.Tool",
|
|
226
|
+
"data": {
|
|
227
|
+
"description": "description",
|
|
228
|
+
"function": "builtins.print",
|
|
229
|
+
"name": "name",
|
|
230
|
+
"parameters": {
|
|
231
|
+
"x": {
|
|
232
|
+
"type": "string",
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
def test_from_dict(self):
|
|
242
|
+
tool = Tool(
|
|
243
|
+
name="name",
|
|
244
|
+
description="description",
|
|
245
|
+
parameters={"x": {"type": "string"}},
|
|
246
|
+
function=print,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
data = {
|
|
250
|
+
"type": "haystack_integrations.components.generators.ollama.chat.chat_generator.OllamaChatGenerator",
|
|
251
|
+
"init_parameters": {
|
|
252
|
+
"timeout": 120,
|
|
253
|
+
"model": "llama2",
|
|
254
|
+
"url": "custom_url",
|
|
255
|
+
"keep_alive": "5m",
|
|
256
|
+
"streaming_callback": "haystack.components.generators.utils.print_streaming_chunk",
|
|
257
|
+
"generation_kwargs": {
|
|
258
|
+
"max_tokens": 10,
|
|
259
|
+
"some_test_param": "test-params",
|
|
260
|
+
},
|
|
261
|
+
"tools": [
|
|
262
|
+
{
|
|
263
|
+
"type": "haystack.tools.tool.Tool",
|
|
264
|
+
"data": {
|
|
265
|
+
"description": "description",
|
|
266
|
+
"function": "builtins.print",
|
|
267
|
+
"name": "name",
|
|
268
|
+
"parameters": {
|
|
269
|
+
"x": {
|
|
270
|
+
"type": "string",
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
}
|
|
278
|
+
component = OllamaChatGenerator.from_dict(data)
|
|
279
|
+
assert component.model == "llama2"
|
|
280
|
+
assert component.streaming_callback is print_streaming_chunk
|
|
281
|
+
assert component.url == "custom_url"
|
|
282
|
+
assert component.keep_alive == "5m"
|
|
283
|
+
assert component.generation_kwargs == {
|
|
284
|
+
"max_tokens": 10,
|
|
285
|
+
"some_test_param": "test-params",
|
|
286
|
+
}
|
|
287
|
+
assert component.timeout == 120
|
|
288
|
+
assert component.tools == [tool]
|
|
289
|
+
|
|
290
|
+
@patch("haystack_integrations.components.generators.ollama.chat.chat_generator.Client")
|
|
291
|
+
def test_run(self, mock_client):
|
|
292
|
+
generator = OllamaChatGenerator()
|
|
293
|
+
|
|
294
|
+
mock_response = ChatResponse(
|
|
295
|
+
model="llama3.2",
|
|
296
|
+
created_at="2023-12-12T14:13:43.416799Z",
|
|
297
|
+
message={
|
|
298
|
+
"role": "assistant",
|
|
299
|
+
"content": "Fine. How can I help you today?",
|
|
300
|
+
},
|
|
301
|
+
done=True,
|
|
302
|
+
total_duration=5191566416,
|
|
303
|
+
load_duration=2154458,
|
|
304
|
+
prompt_eval_count=26,
|
|
305
|
+
prompt_eval_duration=383809000,
|
|
306
|
+
eval_count=298,
|
|
307
|
+
eval_duration=4799921000,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
mock_client_instance = mock_client.return_value
|
|
311
|
+
mock_client_instance.chat.return_value = mock_response
|
|
312
|
+
|
|
313
|
+
result = generator.run(messages=[ChatMessage.from_user("Hello! How are you today?")])
|
|
314
|
+
|
|
315
|
+
mock_client_instance.chat.assert_called_once_with(
|
|
316
|
+
model="orca-mini",
|
|
317
|
+
messages=[{"role": "user", "content": "Hello! How are you today?"}],
|
|
318
|
+
stream=False,
|
|
319
|
+
tools=None,
|
|
320
|
+
options={},
|
|
321
|
+
keep_alive=None,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
assert "replies" in result
|
|
325
|
+
assert len(result["replies"]) == 1
|
|
326
|
+
assert result["replies"][0].text == "Fine. How can I help you today?"
|
|
327
|
+
assert result["replies"][0].role == "assistant"
|
|
328
|
+
|
|
329
|
+
@patch("haystack_integrations.components.generators.ollama.chat.chat_generator.Client")
|
|
330
|
+
def test_run_streaming(self, mock_client):
|
|
331
|
+
streaming_callback_called = False
|
|
332
|
+
|
|
333
|
+
def streaming_callback(_: StreamingChunk) -> None:
|
|
334
|
+
nonlocal streaming_callback_called
|
|
335
|
+
streaming_callback_called = True
|
|
336
|
+
|
|
337
|
+
generator = OllamaChatGenerator(streaming_callback=streaming_callback)
|
|
338
|
+
|
|
339
|
+
mock_response = iter(
|
|
340
|
+
[
|
|
341
|
+
ChatResponse(
|
|
342
|
+
model="llama3.2",
|
|
343
|
+
created_at="2023-12-12T14:13:43.416799Z",
|
|
344
|
+
message={"role": "assistant", "content": "first chunk "},
|
|
345
|
+
done=False,
|
|
346
|
+
),
|
|
347
|
+
ChatResponse(
|
|
348
|
+
model="llama3.2",
|
|
349
|
+
created_at="2023-12-12T14:13:43.416799Z",
|
|
350
|
+
message={"role": "assistant", "content": "second chunk"},
|
|
351
|
+
done=True,
|
|
352
|
+
total_duration=4883583458,
|
|
353
|
+
load_duration=1334875,
|
|
354
|
+
prompt_eval_count=26,
|
|
355
|
+
prompt_eval_duration=342546000,
|
|
356
|
+
eval_count=282,
|
|
357
|
+
eval_duration=4535599000,
|
|
358
|
+
),
|
|
359
|
+
]
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
mock_client_instance = mock_client.return_value
|
|
363
|
+
mock_client_instance.chat.return_value = mock_response
|
|
364
|
+
|
|
365
|
+
result = generator.run(messages=[ChatMessage.from_user("irrelevant")])
|
|
366
|
+
|
|
367
|
+
assert streaming_callback_called
|
|
368
|
+
|
|
369
|
+
assert "replies" in result
|
|
370
|
+
assert len(result["replies"]) == 1
|
|
371
|
+
assert result["replies"][0].text == "first chunk second chunk"
|
|
372
|
+
assert result["replies"][0].role == "assistant"
|
|
373
|
+
|
|
374
|
+
def test_run_fail_with_tools_and_streaming(self, tools):
|
|
375
|
+
component = OllamaChatGenerator(tools=tools, streaming_callback=print_streaming_chunk)
|
|
376
|
+
|
|
377
|
+
with pytest.raises(ValueError):
|
|
378
|
+
message = ChatMessage.from_user("irrelevant")
|
|
379
|
+
component.run([message])
|
|
380
|
+
|
|
381
|
+
@pytest.mark.integration
|
|
382
|
+
def test_live_run(self):
|
|
383
|
+
chat_generator = OllamaChatGenerator(model="llama3.2:3b")
|
|
384
|
+
|
|
385
|
+
user_questions_and_assistant_answers = [
|
|
386
|
+
("What's the capital of France?", "Paris"),
|
|
387
|
+
("What is the capital of Canada?", "Ottawa"),
|
|
388
|
+
("What is the capital of Ghana?", "Accra"),
|
|
389
|
+
]
|
|
390
|
+
|
|
391
|
+
for question, answer in user_questions_and_assistant_answers:
|
|
392
|
+
message = ChatMessage.from_user(question)
|
|
393
|
+
|
|
394
|
+
response = chat_generator.run([message])
|
|
395
|
+
|
|
396
|
+
assert isinstance(response, dict)
|
|
397
|
+
assert isinstance(response["replies"], list)
|
|
398
|
+
assert answer in response["replies"][0].text
|
|
399
|
+
|
|
400
|
+
@pytest.mark.integration
|
|
401
|
+
def test_run_with_chat_history(self):
|
|
402
|
+
chat_generator = OllamaChatGenerator(model="llama3.2:3b")
|
|
403
|
+
|
|
404
|
+
chat_messages = [
|
|
405
|
+
ChatMessage.from_user("What is the largest city in the United Kingdom by population?"),
|
|
406
|
+
ChatMessage.from_assistant("London is the largest city in the United Kingdom by population"),
|
|
407
|
+
ChatMessage.from_user("And what is the second largest?"),
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
response = chat_generator.run(chat_messages)
|
|
411
|
+
|
|
412
|
+
assert isinstance(response, dict)
|
|
413
|
+
assert isinstance(response["replies"], list)
|
|
414
|
+
|
|
415
|
+
assert any(city in response["replies"][-1].text for city in ["Manchester", "Birmingham", "Glasgow"])
|
|
416
|
+
|
|
417
|
+
@pytest.mark.integration
|
|
418
|
+
def test_run_model_unavailable(self):
|
|
419
|
+
component = OllamaChatGenerator(model="unknown_model")
|
|
420
|
+
|
|
421
|
+
with pytest.raises(ResponseError):
|
|
422
|
+
message = ChatMessage.from_user("irrelevant")
|
|
423
|
+
component.run([message])
|
|
424
|
+
|
|
425
|
+
@pytest.mark.integration
|
|
426
|
+
def test_run_with_streaming(self):
|
|
427
|
+
streaming_callback = Mock()
|
|
428
|
+
chat_generator = OllamaChatGenerator(model="llama3.2:3b", streaming_callback=streaming_callback)
|
|
429
|
+
|
|
430
|
+
chat_messages = [
|
|
431
|
+
ChatMessage.from_user("What is the largest city in the United Kingdom by population?"),
|
|
432
|
+
ChatMessage.from_assistant("London is the largest city in the United Kingdom by population"),
|
|
433
|
+
ChatMessage.from_user("And what is the second largest?"),
|
|
434
|
+
]
|
|
435
|
+
|
|
436
|
+
response = chat_generator.run(chat_messages)
|
|
437
|
+
|
|
438
|
+
streaming_callback.assert_called()
|
|
439
|
+
|
|
440
|
+
assert isinstance(response, dict)
|
|
441
|
+
assert isinstance(response["replies"], list)
|
|
442
|
+
assert any(city in response["replies"][-1].text for city in ["Manchester", "Birmingham", "Glasgow"])
|
|
443
|
+
|
|
444
|
+
@pytest.mark.integration
|
|
445
|
+
def test_run_with_tools(self, tools):
|
|
446
|
+
chat_generator = OllamaChatGenerator(model="llama3.2:3b", tools=tools)
|
|
447
|
+
|
|
448
|
+
message = ChatMessage.from_user("What is the weather in Paris?")
|
|
449
|
+
response = chat_generator.run([message])
|
|
450
|
+
|
|
451
|
+
assert len(response["replies"]) == 1
|
|
452
|
+
message = response["replies"][0]
|
|
453
|
+
|
|
454
|
+
assert message.tool_calls
|
|
455
|
+
tool_call = message.tool_call
|
|
456
|
+
assert isinstance(tool_call, ToolCall)
|
|
457
|
+
assert tool_call.tool_name == "weather"
|
|
458
|
+
assert tool_call.arguments == {"city": "Paris"}
|
|
@@ -19,7 +19,7 @@ class TestOllamaGenerator:
|
|
|
19
19
|
("What is the capital of Ghana?", "Accra"),
|
|
20
20
|
]
|
|
21
21
|
|
|
22
|
-
component = OllamaGenerator()
|
|
22
|
+
component = OllamaGenerator(model="llama3.2:3b")
|
|
23
23
|
|
|
24
24
|
for prompt, answer in prompts_and_answers:
|
|
25
25
|
results = component.run(prompt=prompt)
|
|
@@ -147,7 +147,7 @@ class TestOllamaGenerator:
|
|
|
147
147
|
return chunk
|
|
148
148
|
|
|
149
149
|
callback = Callback()
|
|
150
|
-
component = OllamaGenerator(streaming_callback=callback)
|
|
150
|
+
component = OllamaGenerator(model="llama3.2:3b", streaming_callback=callback)
|
|
151
151
|
results = component.run(prompt="What's the capital of Netherlands?")
|
|
152
152
|
|
|
153
153
|
assert len(results["replies"]) == 1
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
from unittest.mock import Mock
|
|
3
|
-
|
|
4
|
-
import pytest
|
|
5
|
-
from haystack.components.generators.utils import print_streaming_chunk
|
|
6
|
-
from haystack.dataclasses import ChatMessage
|
|
7
|
-
from ollama._types import ChatResponse, ResponseError
|
|
8
|
-
|
|
9
|
-
from haystack_integrations.components.generators.ollama import OllamaChatGenerator
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@pytest.fixture
|
|
13
|
-
def chat_messages() -> List[ChatMessage]:
|
|
14
|
-
return [
|
|
15
|
-
ChatMessage.from_user("Tell me about why Super Mario is the greatest superhero"),
|
|
16
|
-
ChatMessage.from_assistant(
|
|
17
|
-
"Super Mario has prevented Bowser from destroying the world", {"something": "something"}
|
|
18
|
-
),
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class TestOllamaChatGenerator:
|
|
23
|
-
def test_init_default(self):
|
|
24
|
-
component = OllamaChatGenerator()
|
|
25
|
-
assert component.model == "orca-mini"
|
|
26
|
-
assert component.url == "http://localhost:11434"
|
|
27
|
-
assert component.generation_kwargs == {}
|
|
28
|
-
assert component.timeout == 120
|
|
29
|
-
assert component.keep_alive is None
|
|
30
|
-
|
|
31
|
-
def test_init(self):
|
|
32
|
-
component = OllamaChatGenerator(
|
|
33
|
-
model="llama2",
|
|
34
|
-
url="http://my-custom-endpoint:11434",
|
|
35
|
-
generation_kwargs={"temperature": 0.5},
|
|
36
|
-
keep_alive="10m",
|
|
37
|
-
timeout=5,
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
assert component.model == "llama2"
|
|
41
|
-
assert component.url == "http://my-custom-endpoint:11434"
|
|
42
|
-
assert component.generation_kwargs == {"temperature": 0.5}
|
|
43
|
-
assert component.timeout == 5
|
|
44
|
-
assert component.keep_alive == "10m"
|
|
45
|
-
|
|
46
|
-
def test_to_dict(self):
|
|
47
|
-
component = OllamaChatGenerator(
|
|
48
|
-
model="llama2",
|
|
49
|
-
streaming_callback=print_streaming_chunk,
|
|
50
|
-
url="custom_url",
|
|
51
|
-
generation_kwargs={"max_tokens": 10, "some_test_param": "test-params"},
|
|
52
|
-
keep_alive="5m",
|
|
53
|
-
)
|
|
54
|
-
data = component.to_dict()
|
|
55
|
-
assert data == {
|
|
56
|
-
"type": "haystack_integrations.components.generators.ollama.chat.chat_generator.OllamaChatGenerator",
|
|
57
|
-
"init_parameters": {
|
|
58
|
-
"timeout": 120,
|
|
59
|
-
"model": "llama2",
|
|
60
|
-
"keep_alive": "5m",
|
|
61
|
-
"url": "custom_url",
|
|
62
|
-
"streaming_callback": "haystack.components.generators.utils.print_streaming_chunk",
|
|
63
|
-
"generation_kwargs": {"max_tokens": 10, "some_test_param": "test-params"},
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
def test_from_dict(self):
|
|
68
|
-
data = {
|
|
69
|
-
"type": "haystack_integrations.components.generators.ollama.chat.chat_generator.OllamaChatGenerator",
|
|
70
|
-
"init_parameters": {
|
|
71
|
-
"timeout": 120,
|
|
72
|
-
"model": "llama2",
|
|
73
|
-
"url": "custom_url",
|
|
74
|
-
"keep_alive": "5m",
|
|
75
|
-
"streaming_callback": "haystack.components.generators.utils.print_streaming_chunk",
|
|
76
|
-
"generation_kwargs": {"max_tokens": 10, "some_test_param": "test-params"},
|
|
77
|
-
},
|
|
78
|
-
}
|
|
79
|
-
component = OllamaChatGenerator.from_dict(data)
|
|
80
|
-
assert component.model == "llama2"
|
|
81
|
-
assert component.streaming_callback is print_streaming_chunk
|
|
82
|
-
assert component.url == "custom_url"
|
|
83
|
-
assert component.generation_kwargs == {"max_tokens": 10, "some_test_param": "test-params"}
|
|
84
|
-
assert component.keep_alive == "5m"
|
|
85
|
-
|
|
86
|
-
def test_build_message_from_ollama_response(self):
|
|
87
|
-
model = "some_model"
|
|
88
|
-
|
|
89
|
-
ollama_response = ChatResponse(
|
|
90
|
-
model=model,
|
|
91
|
-
created_at="2023-12-12T14:13:43.416799Z",
|
|
92
|
-
message={"role": "assistant", "content": "Hello! How are you today?"},
|
|
93
|
-
done=True,
|
|
94
|
-
total_duration=5191566416,
|
|
95
|
-
load_duration=2154458,
|
|
96
|
-
prompt_eval_count=26,
|
|
97
|
-
prompt_eval_duration=383809000,
|
|
98
|
-
eval_count=298,
|
|
99
|
-
eval_duration=4799921000,
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
observed = OllamaChatGenerator(model=model)._build_message_from_ollama_response(ollama_response)
|
|
103
|
-
|
|
104
|
-
assert observed.role == "assistant"
|
|
105
|
-
assert observed.text == "Hello! How are you today?"
|
|
106
|
-
|
|
107
|
-
@pytest.mark.integration
|
|
108
|
-
def test_run(self):
|
|
109
|
-
chat_generator = OllamaChatGenerator()
|
|
110
|
-
|
|
111
|
-
user_questions_and_assistant_answers = [
|
|
112
|
-
("What's the capital of France?", "Paris"),
|
|
113
|
-
("What is the capital of Canada?", "Ottawa"),
|
|
114
|
-
("What is the capital of Ghana?", "Accra"),
|
|
115
|
-
]
|
|
116
|
-
|
|
117
|
-
for question, answer in user_questions_and_assistant_answers:
|
|
118
|
-
message = ChatMessage.from_user(question)
|
|
119
|
-
|
|
120
|
-
response = chat_generator.run([message])
|
|
121
|
-
|
|
122
|
-
assert isinstance(response, dict)
|
|
123
|
-
assert isinstance(response["replies"], list)
|
|
124
|
-
assert answer in response["replies"][0].text
|
|
125
|
-
|
|
126
|
-
@pytest.mark.integration
|
|
127
|
-
def test_run_with_chat_history(self):
|
|
128
|
-
chat_generator = OllamaChatGenerator()
|
|
129
|
-
|
|
130
|
-
chat_history = [
|
|
131
|
-
ChatMessage.from_user("What is the largest city in the United Kingdom by population?"),
|
|
132
|
-
ChatMessage.from_assistant("London is the largest city in the United Kingdom by population"),
|
|
133
|
-
ChatMessage.from_user("And what is the second largest?"),
|
|
134
|
-
]
|
|
135
|
-
|
|
136
|
-
response = chat_generator.run(chat_history)
|
|
137
|
-
|
|
138
|
-
assert isinstance(response, dict)
|
|
139
|
-
assert isinstance(response["replies"], list)
|
|
140
|
-
assert "Manchester" in response["replies"][-1].text or "Glasgow" in response["replies"][-1].text
|
|
141
|
-
|
|
142
|
-
@pytest.mark.integration
|
|
143
|
-
def test_run_model_unavailable(self):
|
|
144
|
-
component = OllamaChatGenerator(model="Alistair_and_Stefano_are_great")
|
|
145
|
-
|
|
146
|
-
with pytest.raises(ResponseError):
|
|
147
|
-
message = ChatMessage.from_user(
|
|
148
|
-
"Based on your infinite wisdom, can you tell me why Alistair and Stefano are so great?"
|
|
149
|
-
)
|
|
150
|
-
component.run([message])
|
|
151
|
-
|
|
152
|
-
@pytest.mark.integration
|
|
153
|
-
def test_run_with_streaming(self):
|
|
154
|
-
streaming_callback = Mock()
|
|
155
|
-
chat_generator = OllamaChatGenerator(streaming_callback=streaming_callback)
|
|
156
|
-
|
|
157
|
-
chat_history = [
|
|
158
|
-
ChatMessage.from_user("What is the largest city in the United Kingdom by population?"),
|
|
159
|
-
ChatMessage.from_assistant("London is the largest city in the United Kingdom by population"),
|
|
160
|
-
ChatMessage.from_user("And what is the second largest?"),
|
|
161
|
-
]
|
|
162
|
-
|
|
163
|
-
response = chat_generator.run(chat_history)
|
|
164
|
-
|
|
165
|
-
streaming_callback.assert_called()
|
|
166
|
-
|
|
167
|
-
assert isinstance(response, dict)
|
|
168
|
-
assert isinstance(response["replies"], list)
|
|
169
|
-
assert "Manchester" in response["replies"][-1].text or "Glasgow" in response["replies"][-1].text
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|