1919
2020import anthropic
2121import pytest
22- from neo4j_graphrag .exceptions import LLMGenerationError
22+ from anthropic import NOT_GIVEN , NotGiven
23+
24+ from neo4j_graphrag .llm import LLMResponse
2325from neo4j_graphrag .llm .anthropic_llm import AnthropicLLM
24- from neo4j_graphrag .llm . types import LLMResponse
26+ from neo4j_graphrag .types import LLMMessage
2527
2628
2729@pytest .fixture
@@ -40,147 +42,82 @@ def test_anthropic_llm_missing_dependency(mock_import: Mock) -> None:
4042 AnthropicLLM (model_name = "claude-3-opus-20240229" )
4143
4244
43- def test_anthropic_invoke_happy_path (mock_anthropic : Mock ) -> None :
44- mock_anthropic .Anthropic .return_value .messages .create .return_value = MagicMock (
45- content = [MagicMock (text = "generated text" )]
46- )
47- model_params = {"temperature" : 0.3 }
48- llm = AnthropicLLM ("claude-3-opus-20240229" , model_params = model_params )
49- input_text = "may thy knife chip and shatter"
50- response = llm .invoke (input_text )
51- assert response .content == "generated text"
52- llm .client .messages .create .assert_called_once_with ( # type: ignore
53- messages = [{"role" : "user" , "content" : input_text }],
54- model = "claude-3-opus-20240229" ,
55- system = anthropic .NOT_GIVEN ,
56- ** model_params ,
57- )
58-
59-
60- def test_anthropic_invoke_with_message_history_happy_path (mock_anthropic : Mock ) -> None :
61- mock_anthropic .Anthropic .return_value .messages .create .return_value = MagicMock (
62- content = [MagicMock (text = "generated text" )]
63- )
64- model_params = {"temperature" : 0.3 }
65- llm = AnthropicLLM (
66- "claude-3-opus-20240229" ,
67- model_params = model_params ,
68- )
45+ def test_anthropic_llm_get_messages_with_system_instructions () -> None :
46+ llm = AnthropicLLM (api_key = "my key" , model_name = "claude" )
6947 message_history = [
70- {"role" : "user" , "content" : "When does the sun come up in the summer?" },
71- {"role" : "assistant" , "content" : "Usually around 6am." },
48+ LLMMessage (** {"role" : "system" , "content" : "do something" }),
49+ LLMMessage (
50+ ** {"role" : "user" , "content" : "When does the sun come up in the summer?" }
51+ ),
52+ LLMMessage (** {"role" : "assistant" , "content" : "Usually around 6am." }),
7253 ]
73- question = "What about next season?"
74-
75- response = llm .invoke (question , message_history ) # type: ignore
76- assert response .content == "generated text"
77- message_history .append ({"role" : "user" , "content" : question })
78- llm .client .messages .create .assert_called_once_with ( # type: ignore[attr-defined]
79- messages = message_history ,
80- model = "claude-3-opus-20240229" ,
81- system = anthropic .NOT_GIVEN ,
82- ** model_params ,
83- )
8454
55+ system_instruction , messages = llm .get_messages (message_history )
56+ assert isinstance (system_instruction , str )
57+ assert system_instruction == "do something"
58+ assert isinstance (messages , list )
59+ assert len (messages ) == 2 # exclude system instruction
60+ for actual , expected in zip (messages , message_history [1 :]):
61+ assert isinstance (actual , dict )
62+ assert actual ["role" ] == expected ["role" ]
63+ assert actual ["content" ] == expected ["content" ]
8564
86- def test_anthropic_invoke_with_system_instruction (
87- mock_anthropic : Mock ,
88- ) -> None :
89- mock_anthropic .Anthropic .return_value .messages .create .return_value = MagicMock (
90- content = [MagicMock (text = "generated text" )]
91- )
92- model_params = {"temperature" : 0.3 }
93- system_instruction = "You are a helpful assistant."
94- llm = AnthropicLLM (
95- "claude-3-opus-20240229" ,
96- model_params = model_params ,
97- )
9865
99- question = "When does it come up in the winter?"
100- response = llm .invoke (question , system_instruction = system_instruction )
101- assert isinstance (response , LLMResponse )
102- assert response .content == "generated text"
103- messages = [{"role" : "user" , "content" : question }]
104- llm .client .messages .create .assert_called_with ( # type: ignore[attr-defined]
105- model = "claude-3-opus-20240229" ,
106- system = system_instruction ,
107- messages = messages ,
108- ** model_params ,
109- )
66+ def test_anthropic_llm_get_messages_without_system_instructions () -> None :
67+ llm = AnthropicLLM (api_key = "my key" , model_name = "claude" )
68+ message_history = [
69+ LLMMessage (
70+ ** {"role" : "user" , "content" : "When does the sun come up in the summer?" }
71+ ),
72+ LLMMessage (** {"role" : "assistant" , "content" : "Usually around 6am." }),
73+ ]
11074
111- assert llm .client .messages .create .call_count == 1 # type: ignore
75+ system_instruction , messages = llm .get_messages (message_history )
76+ assert isinstance (system_instruction , NotGiven )
77+ assert system_instruction == NOT_GIVEN
78+ assert isinstance (messages , list )
79+ assert len (messages ) == 2
80+ for actual , expected in zip (messages , message_history ):
81+ assert isinstance (actual , dict )
82+ assert actual ["role" ] == expected ["role" ]
83+ assert actual ["content" ] == expected ["content" ]
11284
11385
114- def test_anthropic_invoke_with_message_history_and_system_instruction (
115- mock_anthropic : Mock ,
116- ) -> None :
86+ def test_anthropic_invoke_happy_path (mock_anthropic : Mock ) -> None :
11787 mock_anthropic .Anthropic .return_value .messages .create .return_value = MagicMock (
11888 content = [MagicMock (text = "generated text" )]
11989 )
90+ mock_anthropic .types .MessageParam .return_value = {"role" : "user" , "content" : "hi" }
12091 model_params = {"temperature" : 0.3 }
121- system_instruction = "You are a helpful assistant."
122- llm = AnthropicLLM (
123- "claude-3-opus-20240229" ,
124- model_params = model_params ,
125- )
126- message_history = [
127- {"role" : "user" , "content" : "When does the sun come up in the summer?" },
128- {"role" : "assistant" , "content" : "Usually around 6am." },
129- ]
130-
131- question = "When does it come up in the winter?"
132- response = llm .invoke (question , message_history , system_instruction ) # type: ignore
92+ llm = AnthropicLLM ("claude-3-opus-20240229" , model_params = model_params )
93+ input_text = "may thy knife chip and shatter"
94+ response = llm .invoke (input_text )
13395 assert isinstance (response , LLMResponse )
13496 assert response .content == "generated text"
135- message_history . append ({ "role" : "user" , "content" : question })
136- llm . client . messages . create . assert_called_with ( # type: ignore[attr-defined]
97+ llm . client . messages . create . assert_called_once_with ( # type: ignore
98+ messages = [{ "role" : "user" , "content" : "hi" }],
13799 model = "claude-3-opus-20240229" ,
138- system = system_instruction ,
139- messages = message_history ,
100+ system = anthropic .NOT_GIVEN ,
140101 ** model_params ,
141102 )
142103
143- assert llm .client .messages .create .call_count == 1 # type: ignore
144-
145-
146- def test_anthropic_invoke_with_message_history_validation_error (
147- mock_anthropic : Mock ,
148- ) -> None :
149- mock_anthropic .Anthropic .return_value .messages .create .return_value = MagicMock (
150- content = [MagicMock (text = "generated text" )]
151- )
152- model_params = {"temperature" : 0.3 }
153- system_instruction = "You are a helpful assistant."
154- llm = AnthropicLLM (
155- "claude-3-opus-20240229" ,
156- model_params = model_params ,
157- system_instruction = system_instruction ,
158- )
159- message_history = [
160- {"role" : "human" , "content" : "When does the sun come up in the summer?" },
161- {"role" : "assistant" , "content" : "Usually around 6am." },
162- ]
163- question = "What about next season?"
164-
165- with pytest .raises (LLMGenerationError ) as exc_info :
166- llm .invoke (question , message_history ) # type: ignore
167- assert "Input should be 'user', 'assistant' or 'system'" in str (exc_info .value )
168-
169104
170105@pytest .mark .asyncio
171106async def test_anthropic_ainvoke_happy_path (mock_anthropic : Mock ) -> None :
172107 mock_response = AsyncMock ()
173108 mock_response .content = [MagicMock (text = "Return text" )]
174109 mock_model = mock_anthropic .AsyncAnthropic .return_value
175110 mock_model .messages .create = AsyncMock (return_value = mock_response )
111+ mock_anthropic .types .MessageParam .return_value = {"role" : "user" , "content" : "hi" }
176112 model_params = {"temperature" : 0.3 }
177113 llm = AnthropicLLM ("claude-3-opus-20240229" , model_params )
178114 input_text = "may thy knife chip and shatter"
179115 response = await llm .ainvoke (input_text )
116+ assert isinstance (response , LLMResponse )
180117 assert response .content == "Return text"
181118 llm .async_client .messages .create .assert_awaited_once_with ( # type: ignore
182119 model = "claude-3-opus-20240229" ,
183120 system = anthropic .NOT_GIVEN ,
184- messages = [{"role" : "user" , "content" : input_text }],
121+ messages = [{"role" : "user" , "content" : "hi" }],
185122 ** model_params ,
186123 )
0 commit comments