Feature: Base Agent Coverage As a developer I want comprehensive test coverage for the BaseAgent class So that I can ensure the base agent functionality works correctly Background: Given the base agent module is importable And I have a mock LLM provider configured for base agent Scenario: BaseAgent cannot be instantiated directly (abstract class) When I try to instantiate BaseAgent directly Then I should get a TypeError about abstract methods Scenario: Concrete agent can be instantiated with default parameters When I create a concrete agent with default parameters in base agent Then the agent should be initialized successfully in base agent And the agent should have provider "openai" And the agent should have model "gpt-4" And the agent should have temperature 0.7 And the agent should have an llm attribute in base agent And the agent should have a memory attribute in base agent And the agent should have a graph attribute in base agent And the agent should have an app attribute in base agent Scenario: Concrete agent can be instantiated with custom parameters When I create a concrete agent with parameters: in base agent | parameter | value | | provider | anthropic | | model | claude-3 | | temperature | 0.3 | Then the agent should be initialized successfully in base agent And the agent provider should be "anthropic" in base agent And the agent model should be "claude-3" in base agent And the agent temperature should be 0.3 Scenario: Concrete agent accepts provider-specific kwargs When I create a concrete agent with provider kwargs: in base agent | key | value | | max_tokens | 1000 | | top_p | 0.9 | Then the agent should store provider_kwargs correctly in base agent And provider_kwargs should contain "max_tokens" And provider_kwargs should contain "top_p" Scenario: Create OpenAI LLM with correct configuration Given I have a concrete agent instance in base agent When the agent creates an OpenAI LLM with model "gpt-4" and temperature 0.7 Then the LLM should be a ChatOpenAI instance And the LLM creation should be logged in base agent Scenario: Create Anthropic LLM with correct configuration Given I have a concrete agent instance in base agent When I create a concrete agent with provider "anthropic" and model "claude-3" Then the LLM should be a ChatAnthropic instance And the LLM creation should be logged in base agent Scenario: Create Google LLM with correct configuration Given I have a concrete agent instance in base agent When I create a concrete agent with provider "google" and model "gemini-pro" Then the LLM should be a ChatGoogleGenerativeAI instance And the LLM creation should be logged in base agent Scenario: Create LLM with unsupported provider raises ValueError When I try to create a concrete agent with provider "unsupported" Then I should get a ValueError about unsupported provider And the error message should list supported providers in base agent Scenario: Create LLM merges common and provider-specific parameters When I create a concrete agent with model "gpt-4" and provider kwargs: | key | value | | max_tokens | 2000 | Then the LLM should be created with merged parameters in base agent And the merged parameters should include model in base agent And the merged parameters should include temperature in base agent And the merged parameters should include max_tokens in base agent Scenario: Switch provider updates all configuration Given I have a concrete agent with provider "openai" When I switch to provider "anthropic" with model "claude-3" Then the agent provider should be "anthropic" in base agent And the agent model should be "claude-3" in base agent And the llm should be recreated in base agent And the graph should be rebuilt in base agent And the app should be recompiled in base agent And the provider switch should be logged in base agent Scenario: Switch provider with new kwargs updates configuration Given I have a concrete agent with provider "openai" When I switch to provider "google" with model "gemini-pro" and kwargs: | key | value | | max_tokens | 1500 | Then the agent provider_kwargs should contain "max_tokens" And the llm should be recreated in base agent with new configuration Scenario: Invoke executes workflow with input data Given I have a concrete agent instance in base agent When I invoke the agent with input data in base agent: """ { "messages": [{"role": "user", "content": "test"}], "context": {} } """ Then the workflow should execute in base agent And the result should be returned in base agent And the result should contain the expected structure in base agent Scenario: Invoke uses default config when none provided Given I have a concrete agent instance in base agent When I invoke the agent without providing config in base agent Then the default config should be used in base agent And the config should have thread_id "default" Scenario: Invoke uses provided custom config Given I have a concrete agent instance in base agent When I invoke the agent with custom config: in base agent """ { "configurable": {"thread_id": "test-thread-123"} } """ Then the custom config should be used in base agent And the config thread_id should be "test-thread-123" Scenario: Invoke handles exceptions gracefully Given I have a concrete agent instance in base agent When the workflow execution raises an exception in base agent And I invoke the agent with input data in base agent Then the error should be caught in base agent And the result should contain an error field in base agent And the result should contain a result field set to None in base agent And the result should preserve input messages in base agent And the error should be logged in base agent Scenario: Async invoke executes workflow asynchronously Given I have a concrete agent instance in base agent When I ainvoke the agent with input data in base agent: """ { "messages": [{"role": "user", "content": "async test"}], "context": {} } """ Then the async workflow should execute in base agent And the async result should be returned in base agent Scenario: Async invoke uses default config when none provided Given I have a concrete agent instance in base agent When I ainvoke the agent without providing config in base agent Then the default config should be used in base agent for async Scenario: Async invoke uses provided custom config Given I have a concrete agent instance in base agent When I ainvoke the agent with custom config: in base agent """ { "configurable": {"thread_id": "async-thread-456"} } """ Then the custom config should be used in base agent for async Scenario: Async invoke handles exceptions gracefully Given I have a concrete agent instance in base agent When the async workflow execution raises an exception in base agent And I ainvoke the agent with input data in base agent Then the async error should be caught in base agent And the async result should contain an error field in base agent And the async result should preserve input messages in base agent And the async error should be logged in base agent Scenario: Stream yields workflow execution events Given I have a concrete agent instance in base agent When I stream the agent with input data in base agent: """ { "messages": [{"role": "user", "content": "stream test"}], "context": {} } """ Then the workflow should stream events in base agent And each event should be yielded in base agent Scenario: Stream uses default config when none provided Given I have a concrete agent instance in base agent When I stream the agent without providing config in base agent Then the default config should be used in base agent for streaming Scenario: Stream uses provided custom config Given I have a concrete agent instance in base agent When I stream the agent with custom config: in base agent """ { "configurable": {"thread_id": "stream-thread-789"} } """ Then the custom config should be used in base agent for streaming Scenario: Stream handles exceptions gracefully Given I have a concrete agent instance in base agent When the stream execution raises an exception in base agent And I stream the agent with input data in base agent Then the stream error should be caught in base agent And an error event should be yielded in base agent And the stream error should be logged in base agent Scenario: Memory saver is initialized correctly Given I have a concrete agent instance in base agent Then the memory attribute should be a MemorySaver instance in base agent Scenario: Graph is compiled with checkpointer Given I have a concrete agent instance in base agent Then the app should be compiled from the graph in base agent And the app should use the memory checkpointer in base agent Scenario: Build graph is called during initialization When I create a concrete agent with default parameters in base agent Then the build_graph method should be called in base agent And the graph should be stored in base agent Scenario: Multiple provider switches work correctly Given I have a concrete agent with provider "openai" When I switch to provider "anthropic" with model "claude-3" And I switch to provider "google" with model "gemini-pro" a second time in base agent Then the agent provider should be "google" in base agent And the agent model should be "gemini-pro" in base agent And the llm should reflect the latest provider in base agent Scenario: Provider kwargs are preserved between operations Given I have a concrete agent with provider kwargs: in base agent | key | value | | max_tokens | 1000 | When I invoke the agent with input data in base agent Then the provider_kwargs should still contain "max_tokens" Scenario: Logging captures LLM creation details Given logging is enabled at INFO level in base agent When I create a concrete agent with provider "openai" and model "gpt-4" Then the log should contain "Creating openai LLM" in base agent And the log should contain "model=gpt-4" in base agent And the log should contain "temperature=0.7" in base agent Scenario: Logging captures provider switch details Given logging is enabled at INFO level in base agent And I have a concrete agent with provider "openai" When I switch to provider "anthropic" with model "claude-3" Then the log should contain "Switched to anthropic provider" in base agent And the log should contain "model claude-3" in base agent Scenario: Logging captures workflow errors Given logging is enabled at INFO level in base agent And I have a concrete agent instance in base agent When the workflow execution raises an exception in base agent with message "Test error" And I invoke the agent with input data in base agent Then the log should contain "Error executing agent workflow" in base agent And the log should contain "Test error" in base agent Scenario: Logging captures async workflow errors Given logging is enabled at INFO level in base agent And I have a concrete agent instance in base agent When the async workflow execution raises an exception in base agent with message "Async test error" And I ainvoke the agent with input data in base agent Then the log should contain "Error executing agent workflow" in base agent And the log should contain "Async test error" in base agent Scenario: Logging captures stream errors Given logging is enabled at INFO level in base agent And I have a concrete agent instance in base agent When the stream execution raises an exception in base agent with message "Stream test error" And I stream the agent with input data in base agent Then the log should contain "Error streaming agent workflow" in base agent And the log should contain "Stream test error" in base agent Scenario: AgentState TypedDict has correct structure Given I can access AgentState in base agent When I create an AgentState with all fields: in base agent | field | type | | messages | list | | context | dict | | result | dict | | error | str | | metadata | dict | Then the state should store all fields correctly in base agent Scenario: AgentState allows None for optional fields Given I can access AgentState in base agent When I create an AgentState with result None and error None in base agent Then the state should accept None values in base agent Scenario: Concrete agent graph builds with state Given I have a concrete agent instance in base agent Then the graph should be built with AgentState in base agent Scenario: Invoke preserves messages from input in error case Given I have a concrete agent instance in base agent And I have input data with messages: in base agent """ [ {"role": "user", "content": "message 1"}, {"role": "assistant", "content": "message 2"} ] """ When the workflow execution raises an exception in base agent And I invoke the agent with this input data in base agent Then the error result should contain the original messages in base agent Scenario: Temperature parameter is properly stored When I create a concrete agent with temperature 0.1 Then the agent temperature should be 0.1 And the llm should be created with temperature 0.1 Scenario: Temperature parameter has correct default When I create a concrete agent with default parameters in base agent Then the agent temperature should be 0.7 Scenario: Model parameter is properly stored When I create a concrete agent with model "custom-model" Then the agent model should be "custom-model" in base agent And the llm should be created with model "custom-model" Scenario: Real BaseAgent subclass exercises base workflow methods Given I have a real BaseAgent subclass instance in base agent When I invoke the agent without providing config in base agent Then the result should be returned in base agent And the default config should be used in base agent And the real base agent should record invoke thread "default" When I ainvoke the agent without providing config in base agent Then the async result should be returned in base agent And the default config should be used for async in base agent And the real base agent should record async thread "default" When I stream the agent without providing config in base agent Then the default config should be used for streaming in base agent And each event should be yielded in base agent And the real base agent should record stream thread "default" Scenario: Real BaseAgent subclass surfaces workflow errors from base methods Given I have a real BaseAgent subclass instance in base agent When the workflow execution raises an exception in base agent with message "real invoke boom" And I invoke the agent with input data in base agent Then the error should be caught in base agent And the result should contain an error field in base agent And the result should preserve input messages in base agent When the async workflow execution raises an exception in base agent with message "real async boom" And I ainvoke the agent with input data in base agent Then the async error should be caught in base agent And the async result should contain an error field in base agent And the async result should preserve input messages in base agent When the stream execution raises an exception in base agent with message "real stream boom" And I stream the agent with input data in base agent Then the stream error should be caught in base agent And an error event should be yielded in base agent