Using ChatSnowflake with agents

Hi there, im new to the ecosystem and am trying to play with agentic systems with Langchain agents. I’m on my company system and only has access to LLM models hosted on SnowFlake, so I tried using ChatSnowflake with Langchain agents. However, it seems as long as tools are provided to the agent, the agent will not work due to HTTPError 400 (see the attached sample code).

I wonder if ChatSnowflake works with Langchain agents? I dont see any examples other than on Snowflake website, and it doesnt utilize Langhcain agents.

import snowflake.snowpark as snowpark

from langchain_snowflake import ChatSnowflake
from langchain.agents import create_agent
from langchain.messages import SystemMessage, HumanMessage, AIMessage
from langchain.tools import tool

@tool
def get_weather(city: str) -> str:
    """Get weather for a given city."""
    return f"It's always sunny in {city}!"



connection_parameters = {
"account": ...
"user": ...
"authenticator": "externalbrowser",
"role": ...
"warehouse": ...
"database": ...
"schema": ...
}
session = snowpark.Session.builder.configs(connection_parameters).create()
llm = ChatSnowflake(model="llama3.1-8b", session=session, max_tokens=8000,
                    verify_ssl=False)
messages = [
    SystemMessage("You are a helpful assistant"),
    HumanMessage("What's the weather in Singapore?")]


agent = create_agent(llm, name='test')
agent.invoke({'messages': messages})
### works fine, returns something like AIMessage(content="However, I'm a large language model, I don't have real-time access to current weather conditions. But I can suggest some ways fo.....)

agent = create_agent(llm, name='test', tools=[get_weather])
agent.invoke({'messages': messages})
### throws HTTPerror 400

Here’s the full error stack

---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)
Cell In[1], line 61
     57 messages = [
     58     # SystemMessage("You are a helpful assistant"),
     59     HumanMessage("What's the weather in Singapore?")]
---> 61 agent.invoke({'messages': messages})

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langgraph\pregel\main.py:3365, in Pregel.invoke(self, input, config, context, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, durability, version, **kwargs)
   3362             chunks.append(chunk)
   3363 else:
   3364     # v1: collect interrupts from updates stream
-> 3365     for chunk in self.stream(
   3366         input,
   3367         config,
   3368         context=context,
   3369         stream_mode=(
   3370             ["updates", "values"] if stream_mode == "values" else stream_mode
   3371         ),
   3372         print_mode=print_mode,
   3373         output_keys=output_keys,
   3374         interrupt_before=interrupt_before,
   3375         interrupt_after=interrupt_after,
   3376         durability=durability,
   3377         **kwargs,
   3378     ):
   3379         if stream_mode == "values":
   3380             if len(chunk) == 2:

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langgraph\pregel\main.py:2759, in Pregel.stream(self, input, config, context, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, durability, subgraphs, debug, version, **kwargs)
   2757 for task in loop.match_cached_writes():
   2758     loop.output_writes(task.id, task.writes, cached=True)
-> 2759 for _ in runner.tick(
   2760     [t for t in loop.tasks.values() if not t.writes],
   2761     timeout=self.step_timeout,
   2762     get_waiter=get_waiter,
   2763     schedule_task=loop.accept_push,
   2764 ):
   2765     # emit output
   2766     yield from _output(
   2767         stream_mode,
   2768         print_mode,
   (...)   2774         _state_mapper,
   2775     )
   2776 loop.after_tick()

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langgraph\pregel\_runner.py:167, in PregelRunner.tick(self, tasks, reraise, timeout, retry_policy, get_waiter, schedule_task)
    165 t = tasks[0]
    166 try:
--> 167     run_with_retry(
    168         t,
    169         retry_policy,
    170         configurable={
    171             CONFIG_KEY_CALL: partial(
    172                 _call,
    173                 weakref.ref(t),
    174                 retry_policy=retry_policy,
    175                 futures=weakref.ref(futures),
    176                 schedule_task=schedule_task,
    177                 submit=self.submit,
    178             ),
    179         },
    180     )
    181     self.commit(t, None)
    182 except Exception as exc:

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langgraph\pregel\_retry.py:126, in run_with_retry(task, retry_policy, configurable)
    124     task.writes.clear()
    125     # run the task
--> 126     return task.proc.invoke(task.input, config)
    127 except ParentCommand as exc:
    128     ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langgraph\_internal\_runnable.py:656, in RunnableSeq.invoke(self, input, config, **kwargs)
    654     # run in context
    655     with set_config_context(config, run) as context:
--> 656         input = context.run(step.invoke, input, config, **kwargs)
    657 else:
    658     input = step.invoke(input, config)

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langgraph\_internal\_runnable.py:400, in RunnableCallable.invoke(self, input, config, **kwargs)
    398         run_manager.on_chain_end(ret)
    399 else:
--> 400     ret = self.func(*args, **kwargs)
    401 if self.recurse and isinstance(ret, Runnable):
    402     return ret.invoke(input, config)

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain\agents\factory.py:1317, in create_agent.<locals>.model_node(state, runtime)
   1305 request = ModelRequest(
   1306     model=model,
   1307     tools=default_tools,
   (...)   1313     runtime=runtime,
   1314 )
   1316 if wrap_model_call_handler is None:
-> 1317     model_response = _execute_model_sync(request)
   1318     return _build_commands(model_response)
   1320 result = wrap_model_call_handler(request, _execute_model_sync)

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain\agents\factory.py:1289, in create_agent.<locals>._execute_model_sync(request)
   1286 if request.system_message:
   1287     messages = [request.system_message, *messages]
-> 1289 output = model_.invoke(messages)
   1290 if name:
   1291     output.name = name

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_core\language_models\chat_models.py:471, in BaseChatModel.invoke(self, input, config, stop, **kwargs)
    457 @override
    458 def invoke(
    459     self,
   (...)    464     **kwargs: Any,
    465 ) -> AIMessage:
    466     config = ensure_config(config)
    467     return cast(
    468         "AIMessage",
    469         cast(
    470             "ChatGeneration",
--> 471             self.generate_prompt(
    472                 [self._convert_input(input)],
    473                 stop=stop,
    474                 callbacks=config.get("callbacks"),
    475                 tags=config.get("tags"),
    476                 metadata=config.get("metadata"),
    477                 run_name=config.get("run_name"),
    478                 run_id=config.pop("run_id", None),
    479                 **kwargs,
    480             ).generations[0][0],
    481         ).message,
    482     )

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_core\language_models\chat_models.py:1749, in BaseChatModel.generate_prompt(self, prompts, stop, callbacks, **kwargs)
   1740 @override
   1741 def generate_prompt(
   1742     self,
   (...)   1746     **kwargs: Any,
   1747 ) -> LLMResult:
   1748     prompt_messages = [p.to_messages() for p in prompts]
-> 1749     return self.generate(prompt_messages, stop=stop, callbacks=callbacks, **kwargs)

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_core\language_models\chat_models.py:1556, in BaseChatModel.generate(self, messages, stop, callbacks, tags, metadata, run_name, run_id, **kwargs)
   1553 for i, m in enumerate(input_messages):
   1554     try:
   1555         results.append(
-> 1556             self._generate_with_cache(
   1557                 m,
   1558                 stop=stop,
   1559                 run_manager=run_managers[i] if run_managers else None,
   1560                 **kwargs,
   1561             )
   1562         )
   1563     except BaseException as e:
   1564         if run_managers:

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_core\language_models\chat_models.py:1896, in BaseChatModel._generate_with_cache(self, messages, stop, run_manager, **kwargs)
   1894     result = generate_from_stream(iter(chunks))
   1895 elif inspect.signature(self._generate).parameters.get("run_manager"):
-> 1896     result = self._generate(
   1897         messages, stop=stop, run_manager=run_manager, **kwargs
   1898     )
   1899 else:
   1900     result = self._generate(messages, stop=stop, **kwargs)

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_snowflake\chat_models\base.py:334, in ChatSnowflake._generate(self, messages, stop, run_manager, **kwargs)
    332 """Generate a chat response using either REST API (for tools) or SQL function (for basic chat)."""
    333 if self._should_use_rest_api():
--> 334     return self._generate_via_rest_api(messages, stop, run_manager, **kwargs)
    335 else:
    336     return self._generate_via_sql(messages, stop, run_manager, **kwargs)

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_snowflake\chat_models\base.py:529, in ChatSnowflake._generate_via_rest_api(self, messages, stop, run_manager, **kwargs)
    526 payload["stream"] = False
    528 # Make the REST API call using RestApiClient
--> 529 response_data = self._make_rest_api_request(payload)
    531 # Parse the response and handle tool calls
    532 return self._parse_rest_api_response(response_data, messages)

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_snowflake\chat_models\base.py:485, in ChatSnowflake._make_rest_api_request(self, payload)
    475 session = self._get_session()
    477 request_config = RestApiRequestBuilder.cortex_complete_request(
    478     session=session,
    479     method="POST",
   (...)    482     verify_ssl=self.verify_ssl,
    483 )
--> 485 return RestApiClient.make_sync_request(request_config, "Cortex Complete")

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_snowflake\_connection\rest_client.py:137, in RestApiClient.make_sync_request(request_config, operation_name)
    134     return response_data
    136 except Exception as e:
--> 137     SnowflakeErrorHandler.log_and_raise(e, f"sync {operation_name}")

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_snowflake\_error_handling.py:65, in SnowflakeErrorHandler.log_and_raise(error, operation, logger_instance)
     63 log = logger_instance or logger
     64 log.error(f"Error in {operation}: {error}")
---> 65 raise error

File c:\envs\py312_streamlit\.venv\Lib\site-packages\langchain_snowflake\_connection\rest_client.py:116, in RestApiClient.make_sync_request(request_config, operation_name)
    114 # Use requests for sync HTTP
    115 response = getattr(requests, method.lower())(**request_config, timeout=timeout, verify=verify)
--> 116 response.raise_for_status()
    118 # Log response details for debugging
    119 logger.debug(f"Response status: {response.status_code}")

File c:\envs\py312_streamlit\.venv\Lib\site-packages\requests\models.py:1026, in Response.raise_for_status(self)
   1021     http_error_msg = (
   1022         f"{self.status_code} Server Error: {reason} for url: {self.url}"
   1023     )
   1025 if http_error_msg:
-> 1026     raise HTTPError(http_error_msg, response=self)

HTTPError: 400 Client Error: Bad Request for url: *****************************************api/v2/cortex/inference:complete
During task with name 'model' and id '***********************************************'

hi @shilinng89

I see in the doc that llama does not support tool call on Snowflake Cortex Cortex REST API | Snowflake Documentation

swap the model to one that supports tools, e.g.

llm = ChatSnowflake(model="claude-3-5-sonnet", session=session, max_tokens=8000)