tests/unit-tests #22

Open
aditya wants to merge 41 commits from tests/unit-tests into master
Member
No description provided.
ISSUES FIXED: #4
aditya force-pushed tests/unit-tests from 9259325cf3 to 086b8c0a70 2025-11-04 14:38:21 +00:00 Compare
aditya force-pushed tests/unit-tests from 086b8c0a70 to c3889408cf 2025-11-04 15:06:57 +00:00 Compare
brent.edwards requested changes 2025-11-05 02:21:19 +00:00
Dismissed
brent.edwards left a comment
Member

A few first minor comments. I will do more tomorrow.

A few first minor comments. I will do more tomorrow.
README.md Outdated
@ -30,6 +30,13 @@ CleverAgents is a **reactive agent orchestration framework** that combines the p
git clone https://git.cleverthis.com/cleveragents/cleveragents-core
cd cleveragents-core
pip install -e .
<<<<<<< HEAD
Member

This is literally an incomplete merge.

Please finish merging code before you submit it.

This is literally an incomplete merge. Please finish merging code before you submit it.
Author
Member

@brent.edwards
Thank you for pointing that out! I have now resolved the conflicts and pushed the updated commit.

@brent.edwards Thank you for pointing that out! I have now resolved the conflicts and pushed the updated commit.
brent.edwards marked this conversation as resolved
@ -46,7 +46,11 @@ class JinjaYAMLPreprocessor:
) -> Optional[dict[str, Any]]:
"""
Load a YAML file with inline Jinja2 templates.
<<<<<<< HEAD
Member

This code only runs because this merge happens within a comment.

This code only runs because this merge happens within a comment.
Author
Member

@brent.edwards
I have now resolved the conflicts and pushed the updated commit.

@brent.edwards I have now resolved the conflicts and pushed the updated commit.
brent.edwards marked this conversation as resolved
@ -0,0 +308,4 @@
results = []
observer = Observer(
on_next=lambda x: results.append(x),
Member

Very tiny thing: is it possible to replace this with just on_next=results.append without a function call?

Very tiny thing: is it possible to replace this with just `on_next=results.append` without a function call?
Author
Member

@brent.edwards
replaced the lambda with method reference.

@brent.edwards replaced the lambda with method reference.
brent.edwards marked this conversation as resolved
@ -0,0 +385,4 @@
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = None
Member

In lines 386 and 388, loop is never used.

In lines 386 and 388, `loop` is never used.
Author
Member

@brent.edwards
Removed the unused variable.

@brent.edwards Removed the unused variable.
brent.edwards marked this conversation as resolved
@ -0,0 +5,4 @@
"""
import pytest
from typing import Any, Dict, List
Member

Very tiny thing: None of Any, Dict, or List are used.

Very tiny thing: None of `Any`, `Dict`, or `List` are used.
Author
Member

@brent.edwards
Removed the unused variable

@brent.edwards Removed the unused variable
brent.edwards marked this conversation as resolved
@ -0,0 +6,4 @@
import pytest
from typing import Any, Dict, List
from unittest.mock import Mock, patch
Member

Very tiny thing: patch is never used.

Very tiny thing: `patch` is never used.
Author
Member

@brent.edwards
Removed the unused variable

@brent.edwards Removed the unused variable
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-05 22:51:34 +00:00
Dismissed
brent.edwards left a comment
Member

Sending this out, so that if Forgejo goes down, the comments remain.

Sending this out, so that if Forgejo goes down, the comments remain.
@ -0,0 +102,4 @@
agent.dispose()
def test_agent_send_message(self, simple_config, template_renderer):
Member

This test indirectly shows an error in the original code.

It reports:

tests/unit/agents/test_base.py::TestAgent::test_agent_send_message
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/subject/subject.py:105: RuntimeWarning: coroutine 'Agent._process_wrapper' was never awaited
    self.exception = None
  
  Object allocated at:
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 103
      res = hook_impl.function(*args)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/_pytest/python.py", line 159
      result = testfunction(**testargs)
    File "/home/brent.edwards/Workspace/cleveragents-core/tests/unit/agents/test_base.py", line 113
      agent.dispose()
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/agents/base.py", line 166
      self.output_stream.dispose()
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/subject/subject.py", line 105
      self.exception = None

Here's my guess for the cause:

File src/cleveragents/agents/base.py, lines 69-76 is:

    def _setup_processing_pipeline(self) -> None:
        """Set up the reactive processing pipeline."""
        self.input_stream.pipe(
            ops.map(self._process_wrapper),
            ops.flat_map(rx.from_future),
        ).subscribe(
            on_next=self.output_stream.on_next, on_error=self.output_stream.on_error
        )

Notice that the call to self._process_wrapper is not wrapped in asyncio.run.

This test indirectly shows an error in the original code. It reports: ``` tests/unit/agents/test_base.py::TestAgent::test_agent_send_message /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/subject/subject.py:105: RuntimeWarning: coroutine 'Agent._process_wrapper' was never awaited self.exception = None Object allocated at: File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 103 res = hook_impl.function(*args) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/_pytest/python.py", line 159 result = testfunction(**testargs) File "/home/brent.edwards/Workspace/cleveragents-core/tests/unit/agents/test_base.py", line 113 agent.dispose() File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/agents/base.py", line 166 self.output_stream.dispose() File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/subject/subject.py", line 105 self.exception = None ``` Here's my guess for the cause: File `src/cleveragents/agents/base.py`, lines 69-76 is: ``` def _setup_processing_pipeline(self) -> None: """Set up the reactive processing pipeline.""" self.input_stream.pipe( ops.map(self._process_wrapper), ops.flat_map(rx.from_future), ).subscribe( on_next=self.output_stream.on_next, on_error=self.output_stream.on_error ) ``` Notice that the call to `self._process_wrapper` is not wrapped in `asyncio.run`.
Author
Member

Now properly wraps async coroutine in loop.create_task()

Now properly wraps async coroutine in loop.create_task()
brent.edwards marked this conversation as resolved
@ -0,0 +779,4 @@
agent.dispose()
@pytest.mark.asyncio
async def test_map_operator_process_value_and_on_result(self, template_renderer):
Member

This test throws an important warning, but I wasn't able to find a probable cause:

  /home/brent.edwards/.local/share/uv/python/cpython-3.12.10-linux-x86_64-gnu/lib/python3.12/asyncio/events.py:88: RuntimeWarning: coroutine 'StreamableAgent.map_operator.<locals>.process_value' was never awaited
    self._context.run(self._callback, *self._args)
This test throws an important warning, but I wasn't able to find a probable cause: ``` /home/brent.edwards/.local/share/uv/python/cpython-3.12.10-linux-x86_64-gnu/lib/python3.12/asyncio/events.py:88: RuntimeWarning: coroutine 'StreamableAgent.map_operator.<locals>.process_value' was never awaited self._context.run(self._callback, *self._args) ```
Author
Member

map_operator now properly handles async process_value with create_future_task wrapper.

map_operator now properly handles async process_value with create_future_task wrapper.
Member

When I run python -m pytest tests/unit/agents/test_base.py, I get the following error:

tests/unit/agents/test_base.py::TestStreamableAgentMapOperator::test_map_operator_process_value_and_on_result
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/disposable/compositedisposable.py:28: RuntimeWarning: coroutine 'Agent._process_wrapper' was never awaited
    with self.lock:
  
  Object allocated at:
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/observer.py", line 26
      self._on_next_core(value)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/subject/subject.py", line 62
      observer.on_next(value)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py", line 26
      self._on_next(value)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/operators/map.py", line 37
      result = _mapper(value)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/agents/base.py", line 82
      task = loop.create_task(self._process_wrapper(message_data))

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html

Let me know if you want more levels of the callback.

When I run `python -m pytest tests/unit/agents/test_base.py`, I get the following error: ``` tests/unit/agents/test_base.py::TestStreamableAgentMapOperator::test_map_operator_process_value_and_on_result /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/disposable/compositedisposable.py:28: RuntimeWarning: coroutine 'Agent._process_wrapper' was never awaited with self.lock: Object allocated at: File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/observer.py", line 26 self._on_next_core(value) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/subject/subject.py", line 62 observer.on_next(value) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py", line 26 self._on_next(value) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/operators/map.py", line 37 result = _mapper(value) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/agents/base.py", line 82 task = loop.create_task(self._process_wrapper(message_data)) -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html ``` Let me know if you want more levels of the callback.
Author
Member

Added proper cleanup of pending tasks in dispose, cancels any tasks that haven't completed.

Added proper cleanup of pending tasks in dispose, cancels any tasks that haven't completed.
@ -0,0 +836,4 @@
@pytest.mark.asyncio
async def test_filter_operator_filters_values(self, template_renderer):
"""Test filter_operator filters stream values."""
Member

This test shows the same problem as #22/files (comment) :

tests/unit/agents/test_base.py::TestStreamableAgentFilterOperator::test_filter_operator_filters_values
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/scheduler/currentthreadscheduler.py:29: RuntimeWarning: coroutine 'Agent._process_wrapper' was never awaited
    @classmethod
This test shows the same problem as https://git.cleverthis.com/cleveragents/cleveragents-core/pulls/22/files#issuecomment-31569 : ``` tests/unit/agents/test_base.py::TestStreamableAgentFilterOperator::test_filter_operator_filters_values /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/scheduler/currentthreadscheduler.py:29: RuntimeWarning: coroutine 'Agent._process_wrapper' was never awaited @classmethod ```
Member

And it also has one other missing await (or other call):

tests/unit/agents/test_base.py::TestStreamableAgentFilterOperator::test_filter_operator_filters_values
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py:26: RuntimeWarning: coroutine 'StreamableAgent.filter_operator.<locals>.filter_with_agent' was never awaited
    self._on_next(value)
...
    File "/home/brent.edwards/.local/share/uv/python/cpython-3.12.10-linux-x86_64-gnu/lib/python3.12/asyncio/base_events.py", line 1999
      handle._run()
    File "/home/brent.edwards/.local/share/uv/python/cpython-3.12.10-linux-x86_64-gnu/lib/python3.12/asyncio/events.py", line 88
      self._context.run(self._callback, *self._args)
    File "/home/brent.edwards/Workspace/cleveragents-core/tests/unit/agents/test_base.py", line 865
      filtered.subscribe(on_next=on_next, on_completed=on_completed)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observable/observable.py", line 96
      return self.subscribe_(on_next, on_error, on_completed, scheduler)
And it also has one other missing `await` (or other call): ``` tests/unit/agents/test_base.py::TestStreamableAgentFilterOperator::test_filter_operator_filters_values /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py:26: RuntimeWarning: coroutine 'StreamableAgent.filter_operator.<locals>.filter_with_agent' was never awaited self._on_next(value) ... File "/home/brent.edwards/.local/share/uv/python/cpython-3.12.10-linux-x86_64-gnu/lib/python3.12/asyncio/base_events.py", line 1999 handle._run() File "/home/brent.edwards/.local/share/uv/python/cpython-3.12.10-linux-x86_64-gnu/lib/python3.12/asyncio/events.py", line 88 self._context.run(self._callback, *self._args) File "/home/brent.edwards/Workspace/cleveragents-core/tests/unit/agents/test_base.py", line 865 filtered.subscribe(on_next=on_next, on_completed=on_completed) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observable/observable.py", line 96 return self.subscribe_(on_next, on_error, on_completed, scheduler) ```
Author
Member

filter_operator now properly handles async filter_with_agent with create_future_task wrapper

filter_operator now properly handles async filter_with_agent with create_future_task wrapper
brent.edwards marked this conversation as resolved
@ -0,0 +876,4 @@
agent.dispose()
@pytest.mark.asyncio
async def test_filter_operator_with_false_condition(self, template_renderer):
Member

This shows the same problem as #22/files (comment) :


tests/unit/agents/test_base.py::TestStreamableAgentFilterOperator::test_filter_operator_with_false_condition
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py:26: RuntimeWarning: coroutine 'StreamableAgent.filter_operator.<locals>.filter_with_agent' was never awaited
    self._on_next(value)
This shows the same problem as https://git.cleverthis.com/cleveragents/cleveragents-core/pulls/22/files#issuecomment-31572 : ``` tests/unit/agents/test_base.py::TestStreamableAgentFilterOperator::test_filter_operator_with_false_condition /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py:26: RuntimeWarning: coroutine 'StreamableAgent.filter_operator.<locals>.filter_with_agent' was never awaited self._on_next(value) ```
brent.edwards marked this conversation as resolved
@ -0,0 +919,4 @@
agent.dispose()
@pytest.mark.asyncio
async def test_filter_operator_sets_result_once(self, template_renderer):
Member

This test shows the same problem as several others above:

tests/unit/agents/test_base.py::TestStreamableAgentFilterOperator::test_filter_operator_sets_result_once
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py:26: RuntimeWarning: coroutine 'StreamableAgent.filter_operator.<locals>.filter_with_agent' was never awaited
    self._on_next(value)
This test shows the same problem as several others above: ``` tests/unit/agents/test_base.py::TestStreamableAgentFilterOperator::test_filter_operator_sets_result_once /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py:26: RuntimeWarning: coroutine 'StreamableAgent.filter_operator.<locals>.filter_with_agent' was never awaited self._on_next(value) ```
brent.edwards marked this conversation as resolved
@ -0,0 +7,4 @@
import pytest
import asyncio
from typing import Any, Dict, List, Optional
from unittest.mock import Mock, AsyncMock, patch
Member

Tiny thing: patch is never used.

Tiny thing: `patch` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +187,4 @@
assert "test message" in result
@pytest.mark.asyncio
async def test_process_via_agent_with_routing(self, simple_config, template_renderer):
Member

Tiny thing: simple_config is never used.

Tiny thing: `simple_config` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +202,4 @@
assert "MockAgent agent1" in result
@pytest.mark.asyncio
async def test_process_via_agent_missing(self, simple_config, template_renderer):
Member

Tiny thing: simple_config is never used.

Tiny thing: `simple_config` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +266,4 @@
assert "LangGraph bridge not available" in str(exc_info.value)
@pytest.mark.asyncio
async def test_process_via_graph_with_bridge(self, template_renderer):
Member

Tiny thing: template_renderer is never used.

Tiny thing: `template_renderer` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +292,4 @@
assert result == "graph result"
@pytest.mark.asyncio
async def test_process_via_graph_from_bridge(self, template_renderer):
Member

Tiny thing: template_renderer is never used.

Tiny thing: `template_renderer` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +365,4 @@
assert "composite" in capabilities
def test_get_capabilities_with_agents(self, simple_config, template_renderer):
Member

There are two methods named test_get_capabilities_with_agents. This one and the one on line 516.

There are two methods named `test_get_capabilities_with_agents`. This one and the one on line 516.
brent.edwards marked this conversation as resolved
@ -0,0 +447,4 @@
assert result == "graph result"
@pytest.mark.asyncio
async def test_process_graph_non_dict_result(self, template_renderer):
Member

Tiny thing: template_renderer is never used.

Tiny thing: `template_renderer` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +513,4 @@
assert "test_stream" in composite.components["streams"]
assert composite.components["streams"]["test_stream"] == mock_stream
def test_get_capabilities_with_agents(self, template_renderer):
Member

There are two methods named test_get_capabilities_with_agents. This one and the one on line 368.

There are two methods named `test_get_capabilities_with_agents`. This one and the one on line 368.
brent.edwards marked this conversation as resolved
@ -0,0 +560,4 @@
# Start the coroutine but don't wait for it to complete
# since we need to timeout
try:
result = await asyncio.wait_for(
Member

Did you ever intend to use result?

If not, result is never used.

Did you ever intend to use `result`? If not, `result` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +625,4 @@
@pytest.mark.asyncio
async def test_process_via_stream_with_timeout(self, template_renderer):
"""Test processing via stream with timeout."""
config = {
Member

Lines 628-649 are exactly the same as lines 658-679 and lines 702-723. If you can, they should all be turned into a method.

Lines 628-649 are exactly the same as lines 658-679 and lines 702-723. If you can, they should all be turned into a method.
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-05 23:32:53 +00:00
Dismissed
brent.edwards left a comment
Member

Stopping again in case Forgejo loses my comments.

Stopping again in case Forgejo loses my comments.
@ -0,0 +6,4 @@
import pytest
from io import StringIO
import sys
Member

sys is never used; you can remove it.

`sys` is never used; you can remove it.
brent.edwards marked this conversation as resolved
@ -0,0 +83,4 @@
def raises_error():
raise ValueError("Test error")
captured = capsys.readouterr()
Member

captured is never used. Maybe you intended to have a test next line?

`captured` is never used. Maybe you intended to have a test next line?
brent.edwards marked this conversation as resolved
@ -0,0 +5,4 @@
"""
import pytest
from typing import Any, Dict, Optional
Member

None of Any, Dict, or Optional are used in this code; you can remove this import.

None of `Any`, `Dict`, or `Optional` are used in this code; you can remove this import.
brent.edwards marked this conversation as resolved
@ -0,0 +139,4 @@
def test_factory_create_agent_missing_type_defaults_to_llm(self, template_renderer, monkeypatch):
"""Test creating an agent with missing type defaults to LLM."""
import os
Member

You never use os. You can eliminate this line.

You never use `os`. You can eliminate this line.
brent.edwards marked this conversation as resolved
@ -0,0 +160,4 @@
agent = factory.create_agent("test_agent")
# Should create an LLM agent by default
from cleveragents.agents.llm import LLMAgent
Member

You already imported LLMAgent in line 13.

You already imported `LLMAgent` in line 13.
brent.edwards marked this conversation as resolved
@ -0,0 +235,4 @@
# Create all agents
agent1 = factory.create_agent("llm1")
agent2 = factory.create_agent("llm2")
agent3 = factory.create_agent("tool1")
Member

None of the variables agent1, agent2, or agent3 are used. You can remove the variables (or use them for testing.)

None of the variables `agent1`, `agent2`, or `agent3` are used. You can remove the variables (or use them for testing.)
brent.edwards marked this conversation as resolved
@ -0,0 +392,4 @@
def test_factory_create_agents_from_config(self, template_renderer, monkeypatch):
"""Test creating all agents from config."""
monkeypatch.setenv("OPENAI_API_KEY", "fake-key")
Member

Lines 395-404 and 415-424 are identical. You might want to create a method to create factory.

Lines 395-404 and 415-424 are identical. You might want to create a method to create `factory`.
brent.edwards marked this conversation as resolved
@ -0,0 +533,4 @@
factory = AgentFactory(config, template_renderer)
from cleveragents.core.exceptions import ConfigurationError
Member

You already imported ConfigurationError in line 15. You can remove this.

You already imported `ConfigurationError` in line 15. You can remove this.
brent.edwards marked this conversation as resolved
@ -0,0 +545,4 @@
factory = AgentFactory(config, template_renderer)
from cleveragents.core.exceptions import ConfigurationError
Member

You already imported ConfigurationError in line 15. You can remove this line.

You already imported `ConfigurationError` in line 15. You can remove this line.
brent.edwards marked this conversation as resolved
@ -0,0 +557,4 @@
factory = AgentFactory(config, template_renderer)
from cleveragents.core.exceptions import ConfigurationError
Member

You already imported ConfigurationError in line 15. You can remove this line.

You already imported `ConfigurationError` in line 15. You can remove this line.
brent.edwards marked this conversation as resolved
@ -0,0 +569,4 @@
factory = AgentFactory(config, template_renderer)
from cleveragents.core.exceptions import ConfigurationError
Member

You already imported ConfigurationError in line 15. You can remove this line.

You already imported `ConfigurationError` in line 15. You can remove this line.
brent.edwards marked this conversation as resolved
@ -0,0 +581,4 @@
factory = AgentFactory(config, template_renderer)
from cleveragents.core.exceptions import ConfigurationError
Member

You already imported ConfigurationError in line 15. You can remove this line.

You already imported `ConfigurationError` in line 15. You can remove this line.
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-06 00:11:19 +00:00
Dismissed
brent.edwards left a comment
Member

Another easy stop point.

Another easy stop point.
@ -0,0 +5,4 @@
"""
import pytest
from typing import Any, Dict, List, Optional
Member

None of Any, Dict, List, or Optional are used. You can remove this line.

None of `Any`, `Dict`, `List`, or `Optional` are used. You can remove this line.
brent.edwards marked this conversation as resolved
@ -0,0 +6,4 @@
import pytest
from typing import Any, Dict, List, Optional
from unittest.mock import Mock, patch, MagicMock, AsyncMock
Member

MagicMock is never used. You can remove it.

`MagicMock` is never used. You can remove it.
brent.edwards marked this conversation as resolved
@ -0,0 +182,4 @@
config = {"type": "llm", "provider": "unsupported"}
with pytest.raises(ConfigurationError) as exc_info:
agent = LLMAgent("test", config, template_renderer)
Member

agent is never used; you can remove that variable.

`agent` is never used; you can remove that variable.
brent.edwards marked this conversation as resolved
@ -0,0 +363,4 @@
# Verify the model was called with history
call_args = mock_model.ainvoke.call_args[0][0]
assert len(call_args) >= 3 # System + 2 history + current
Member

Why is this >= 3 instead of an exact value?

Why is this `>= 3` instead of an exact value?
brent.edwards marked this conversation as resolved
@ -0,0 +423,4 @@
# Verify history was included in the call
call_args = mock_model.ainvoke.call_args[0][0]
assert len(call_args) > 3 # Has history messages
Member

Why isn't this an exact number?

Why isn't this an exact number?
brent.edwards marked this conversation as resolved
@ -0,0 +28,4 @@
renderer = TemplateRenderer(TemplateEngine.JINJA2)
with patch('cleveragents.agents.llm.ChatOpenAI') as mock_openai:
agent = LLMAgent("test_agent", config, renderer)
Member

agent is never used.

`agent` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +45,4 @@
renderer = TemplateRenderer(TemplateEngine.JINJA2)
with patch('cleveragents.agents.llm.ChatAnthropic') as mock_anthropic:
agent = LLMAgent("test_agent", config, renderer)
Member

agent is never used here, either.

`agent` is never used here, either.
brent.edwards marked this conversation as resolved
@ -0,0 +62,4 @@
renderer = TemplateRenderer(TemplateEngine.JINJA2)
with patch('cleveragents.agents.llm.ChatGoogleGenerativeAI') as mock_google:
agent = LLMAgent("test_agent", config, renderer)
Member

agent is never used here, again.

`agent` is never used here, again.
brent.edwards marked this conversation as resolved
@ -0,0 +93,4 @@
agent = LLMAgent("test_agent", config, renderer)
# Process a message
response = await agent.process_message("Test input message")
Member

response is never used, either.

`response` is never used, either.
brent.edwards marked this conversation as resolved
@ -0,0 +130,4 @@
# Should be trimmed to max_history (4 messages)
# Each message creates 2 entries (user + assistant), so 4 messages = 8 entries
assert len(history) <= 8
Member

Why is this <= instead of ==?

Why is this `<=` instead of `==`?
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-06 01:18:19 +00:00
Dismissed
brent.edwards left a comment
Member

Another stopping point.

Another stopping point.
@ -0,0 +5,4 @@
"""
import pytest
import json
Member

json is never used.

`json` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +6,4 @@
import pytest
import json
import asyncio
Member

asyncio is never used.

`asyncio` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +7,4 @@
import pytest
import json
import asyncio
from typing import Any, Dict
Member

Neither Any nor Dict are used anywhere.

Neither `Any` nor `Dict` are used anywhere.
brent.edwards marked this conversation as resolved
@ -0,0 +9,4 @@
import asyncio
from typing import Any, Dict
from unittest.mock import Mock, patch, AsyncMock, mock_open
from pathlib import Path
Member

Path is never used.

`Path` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +1002,4 @@
try:
for cmd, args in dangerous_commands:
try:
result = await agent._execute_shell_command(cmd, args)
Member

result is never used.

`result` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +11,4 @@
"""
import pytest
from unittest.mock import Mock, patch, AsyncMock
Member

Neither Mock, patch, nor AsyncMock are used.

Neither `Mock`, `patch`, nor `AsyncMock` are used.
brent.edwards marked this conversation as resolved
@ -0,0 +34,4 @@
"/tmp/tmp*.j2",
"/var/tmp/tmp*.yaml",
"/var/tmp/tmp*.json"
]
Member

I am nervous about these file names -- and that you delete ALL of the ones created in the last hour.

It seems really likely that other programs will create files with the same names. You would be deleting files that other programs might use.

The best solution is to maintain what files you create and delete them. If possible, each test should clean up after itself.

The second best solution would to use a common prefix like cleveragent.

I am nervous about these file names -- and that you delete ALL of the ones created in the last hour. It seems really likely that other programs will create files with the same names. You would be deleting files that other programs might use. The best solution is to maintain what files you create and delete them. If possible, each test should clean up after itself. The second best solution would to use a common prefix like `cleveragent`.
brent.edwards marked this conversation as resolved
@ -0,0 +1053,4 @@
assert result == "# Section Header"
@pytest.mark.asyncio
async def test_prepare_append_content_non_section(self, template_renderer, tmp_path):
Member

I'm sorry that I missed this last time; tmp_path is not used.

I'm sorry that I missed this last time; `tmp_path` is not used.
Author
Member

Removed unused parameter from both test instances

Removed unused parameter from both test instances
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-06 23:40:58 +00:00
Dismissed
brent.edwards left a comment
Member

I know that you didn't write this -- Jeff did -- but are the temporary files in tests/features/steps/routes_steps.py cleaned up?

I know that you didn't write this -- Jeff did -- but are the temporary files in `tests/features/steps/routes_steps.py` cleaned up?
@ -68,8 +68,13 @@ class Agent(ABC):
def _setup_processing_pipeline(self) -> None:
"""Set up the reactive processing pipeline."""
def create_future(message_data):
Member

pylint:

src/cleveragents/agents/base.py:71: error: Function is missing a type annotation  [no-untyped-def]
pylint: ``` src/cleveragents/agents/base.py:71: error: Function is missing a type annotation [no-untyped-def] ```
Author
Member

All 5 instances in routes_steps.py have prefix="cleveragent_" for proper cleanup via conftest.py ; Added message_data: tuple[str, dict[str, Any]] to create_future

All 5 instances in routes_steps.py have prefix="cleveragent_" for proper cleanup via conftest.py ; Added message_data: tuple[str, dict[str, Any]] to create_future
brent.edwards marked this conversation as resolved
@ -69,2 +69,4 @@
def _setup_processing_pipeline(self) -> None:
"""Set up the reactive processing pipeline."""
def create_future(message_data):
# Create a future and schedule the coroutine on the event loop
Member

Please change this from a comment to a docstring. (Just change this line to

" Create a future and schedule the coroutine on the event loop "

and you're perfect.)

Please change this from a comment to a docstring. (Just change this line to ``` " Create a future and schedule the coroutine on the event loop " ``` and you're perfect.)
Author
Member

Converted all 3 comment instances to proper docstrings

Converted all 3 comment instances to proper docstrings
brent.edwards marked this conversation as resolved
@ -347,2 +352,3 @@
return ops.map(process_value)
def create_future_task(value: Any) -> Any:
# Create a task in the current event loop
Member

Please change this from a comment to a docstring.

Please change this from a comment to a docstring.
brent.edwards marked this conversation as resolved
@ -373,2 +384,3 @@
return ops.filter(filter_with_agent) # type: ignore[arg-type]
def create_future_task(value: Any) -> Any:
# Create a task in the current event loop
Member

Please change this from a comment to a docstring.

Please change this from a comment to a docstring.
Author
Member

Done!

Done!
brent.edwards marked this conversation as resolved
@ -375,0 +387,4 @@
loop = asyncio.get_event_loop()
return loop.create_task(filter_with_agent(value))
def extract_value(value_bool_tuple: tuple[Any, bool]) -> Any:
Member

Please add a docstring.

Please add a docstring.
Author
Member

Done!

Done!
brent.edwards marked this conversation as resolved
@ -375,0 +390,4 @@
def extract_value(value_bool_tuple: tuple[Any, bool]) -> Any:
return value_bool_tuple[0]
def composed_operator(source: Observable) -> Observable:
Member

Please add a docstring.

Please add a docstring.
Author
Member

Done!

Done!
brent.edwards marked this conversation as resolved
@ -933,3 +933,3 @@
# Create temporary file
temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False)
temp_file = tempfile.NamedTemporaryFile(mode="w", prefix='cleveragent_', suffix=".yaml", delete=False)
Member

YOU DO NOT NEED TO DO ANYTHING. THIS IS JUST A QUICK COMMENT...

When you have to make the same change over and over, even if it's something like adding a parameter to tempfile.NamedTemporaryFile, it's often easier to create a function and replace the call to tempfile.NamedTemporaryFile to that function.

Why?

Because the parts of code that you change once are the parts of code most likely to need to change again.

THIS IS JUST AN IDEA; I DO NOT EXPECT YOU TO MAKE THIS CHANGE.

YOU DO NOT NEED TO DO ANYTHING. THIS IS JUST A QUICK COMMENT... When you have to make the same change over and over, even if it's something like adding a parameter to `tempfile.NamedTemporaryFile`, it's often easier to create a function and replace the call to `tempfile.NamedTemporaryFile` to that function. Why? Because the parts of code that you change once are the parts of code most likely to need to change again. THIS IS JUST AN IDEA; I DO NOT EXPECT YOU TO MAKE THIS CHANGE.
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-07 00:59:12 +00:00
Dismissed
brent.edwards left a comment
Member

A lot of comments for a complex file.

A lot of comments for a complex file.
@ -0,0 +9,4 @@
import pytest
import tempfile
from pathlib import Path
from unittest.mock import Mock, patch, MagicMock, AsyncMock
Member

MagicMock is never used.

`MagicMock` is never used.
Author
Member

removed unused variables.

removed unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +10,4 @@
import tempfile
from pathlib import Path
from unittest.mock import Mock, patch, MagicMock, AsyncMock
from typing import Any, Dict
Member

Neither 'Any' nor 'dict' are used in this code.

Neither 'Any' nor 'dict' are used in this code.
Author
Member

removed unused variables.

removed unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +16,4 @@
from cleveragents.core.exceptions import (
UnsafeConfigurationError,
CleverAgentsException,
ConfigurationError,
Member

ConfigurationError is never used.

`ConfigurationError` is never used.
Author
Member

removed unused variables.

removed unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +19,4 @@
ConfigurationError,
)
from cleveragents.reactive.route import RouteType
from cleveragents.reactive.stream_router import StreamType
Member

StreamType is never used.

`StreamType` is never used.
Author
Member

removed unused variables.

removed unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +25,4 @@
class TestReactiveCleverAgentsAppInitialization:
"""Test suite for ReactiveCleverAgentsApp initialization."""
def test_initialization_without_config(self):
Member

While running this test, I got the following error message:

tests/unit/core/test_application.py::TestReactiveCleverAgentsAppInitialization::test_initialization_without_config
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/core/application.py:98: DeprecationWarning: There is no current event loop
    self.loop = asyncio.get_event_loop()
While running this test, I got the following error message: ``` tests/unit/core/test_application.py::TestReactiveCleverAgentsAppInitialization::test_initialization_without_config /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/core/application.py:98: DeprecationWarning: There is no current event loop self.loop = asyncio.get_event_loop() ```
Author
Member

the code now uses asyncio.get_running_loop() with proper fallback to asyncio.new_event_loop()

the code now uses asyncio.get_running_loop() with proper fallback to asyncio.new_event_loop()
brent.edwards marked this conversation as resolved
@ -0,0 +303,4 @@
result = app._process_tool_commands(content)
assert "Error" in result or "" in result or "Invalid" in result
Member

This check makes me a little nervous. Is the test tool using an LLM or in some other way nondeterministic?

This check makes me a little nervous. Is the test tool using an LLM or in some other way nondeterministic?
Author
Member

All "or" conditions removed - 13 tests now check specific error messages

All "or" conditions removed - 13 tests now check specific error messages
brent.edwards marked this conversation as resolved
@ -0,0 +318,4 @@
assert isinstance(result, str)
# Should indicate tool not found or execution limited
assert "not found" in result.lower() or "error" in result.lower()
Member

Again -- that we don't know what the result should be when a tool doesn't exist makes me nervous.

I'm not going to point them all out. It might be that these results come from an LLM, so the LLM might give multiple answers. But if it doesn't, could you check what the actual answer should be?

(You can find where I'm nervous by looking for assert statements that have an or in them. If the result comes from an LLM, then this problem might not be solvable. But if the result comes from an LLM, then move the test to the integration tests, please.)

Again -- that we don't know what the result should be when a tool doesn't exist makes me nervous. I'm not going to point them all out. It might be that these results come from an LLM, so the LLM might give multiple answers. But if it doesn't, could you check what the actual answer should be? (You can find where I'm nervous by looking for `assert` statements that have an `or` in them. If the result comes from an LLM, then this problem might not be solvable. But if the result comes from an LLM, then move the test to the integration tests, please.)
brent.edwards marked this conversation as resolved
@ -0,0 +468,4 @@
app = ReactiveCleverAgentsApp()
# Should handle gracefully
app._handle_stream_command("unknown_command_xyz")
Member

Is the point of this test just that _handle_stream_command does not throw an error?

Is the point of this test just that `_handle_stream_command` does not throw an error?
Author
Member

test_handle_stream_command_unknown: Verifies graceful handling - asserts usage message printed when command format is invalid

test_handle_stream_command_unknown: Verifies graceful handling - asserts usage message printed when command format is invalid
brent.edwards marked this conversation as resolved
@ -0,0 +482,4 @@
app = ReactiveCleverAgentsApp(config_files=None, unsafe=False)
import io
import sys
Member

You import io and sys on lines 484-485, 504-505, 529-530, and 1659-1650. Why isn't this at the top of the file, just once?

You import `io` and `sys` on lines 484-485, 504-505, 529-530, and 1659-1650. Why isn't this at the top of the file, just once?
Author
Member

io and sys moved to top of file

io and sys moved to top of file
brent.edwards marked this conversation as resolved
@ -0,0 +566,4 @@
def temp_config_file(self):
"""Create a temporary valid config file."""
with tempfile.NamedTemporaryFile(mode='w', prefix='cleveragent_', suffix='.yaml', delete=False) as f:
f.write("""
Member

Why is this code writing this file instead of providing it with the tests? (Same question for lines 623-637, and everywhere throughout the code where the code contains with tempfile.NamedTemporaryFile.

It makes sense to dynamically create files if the files will change every time that you run it. But it doesn't make sense to dynamically create files if the files never change.

Why is this code writing this file instead of providing it with the tests? (Same question for lines 623-637, and everywhere throughout the code where the code contains `with tempfile.NamedTemporaryFile`. It makes sense to dynamically create files if the files will change every time that you run it. But it doesn't make sense to dynamically create files if the files never change.
Author
Member

All tempfile.NamedTemporaryFile instances removed and replaced with static fixture files in tests/fixtures/ directory

All tempfile.NamedTemporaryFile instances removed and replaced with static fixture files in tests/fixtures/ directory
brent.edwards marked this conversation as resolved
@ -0,0 +617,4 @@
nonexistent_path = Path("/nonexistent/path/config.yaml")
with pytest.raises((CleverAgentsException, FileNotFoundError, Exception)):
Member

It makes me slightly nervous that the test doesn't know which exception to expect...

It makes me slightly nervous that the test doesn't know which exception to expect...
Author
Member

Changed from pytest.raises((CleverAgentsException, FileNotFoundError, Exception)) to pytest.raises(CleverAgentsException) with specific error message assertion

Changed from pytest.raises((CleverAgentsException, FileNotFoundError, Exception)) to pytest.raises(CleverAgentsException) with specific error message assertion
brent.edwards marked this conversation as resolved
@ -0,0 +794,4 @@
def test_setup_routes_with_template_config_having_template(self):
"""Test setup routes WITH template_config attribute."""
from cleveragents.reactive.route import RouteType
Member

This had already been imported on line 21.

This had already been imported on line 21.
Author
Member

All imports moved to top of file

All imports moved to top of file
brent.edwards marked this conversation as resolved
@ -0,0 +795,4 @@
def test_setup_routes_with_template_config_having_template(self):
"""Test setup routes WITH template_config attribute."""
from cleveragents.reactive.route import RouteType
from cleveragents.reactive.stream_router import StreamType
Member

This had already been imported on line 22.

This had already been imported on line 22.
Author
Member

All imports moved to top of file

All imports moved to top of file
brent.edwards marked this conversation as resolved
@ -0,0 +841,4 @@
def test_setup_routes_with_graph_type(self):
"""Test setup routes with GRAPH route type."""
from cleveragents.reactive.route import RouteType
from cleveragents.langgraph.graph import GraphConfig
Member

These two lines had already been imported on line 21.

These two lines had already been imported on line 21.
Author
Member

All imports moved to top of file

All imports moved to top of file
Member

Look at lines 340, 341, 701, 732, 733, 868, 988, 1225, 1240, 1255, 1270, 1309, 1351, 1645, 1674, 1717, 1796, 2046, 2085.

Those are all imports from cleveragents. You might have a reason why they should only be imported when they're needed, so I didn't comment on them before. But at best all standard imports moved to the top of the file.

Look at lines 340, 341, 701, 732, 733, 868, 988, 1225, 1240, 1255, 1270, 1309, 1351, 1645, 1674, 1717, 1796, 2046, 2085. Those are all imports from `cleveragents`. You might have a reason why they should only be imported when they're needed, so I didn't comment on them before. But at best all standard imports moved to the top of the file.
brent.edwards marked this conversation as resolved
@ -0,0 +1060,4 @@
assert app.template_renderer is not None
except Exception as e:
# Some parts might still fail due to deep dependencies
# but at least the parsing was attempted
Member

This statement worries me a lot. Are you able to set up this test correctly?

Do we need to move to a more functional-style approach?

This statement worries me a lot. Are you able to set up this test correctly? Do we need to move to a more functional-style approach?
Author
Member

test now has clean mocking without catching exceptions

test now has clean mocking without catching exceptions
brent.edwards marked this conversation as resolved
@ -0,0 +1080,4 @@
try:
app.load_configuration([temp_config_file])
except Exception:
pass # Might still fail, but check if methods were called
Member

Again -- this makes me nervous. Are we able to load the configuration without exceptions?

Again -- this makes me nervous. Are we able to load the configuration without exceptions?
Author
Member

test now properly validates without exception handling

test now properly validates without exception handling
brent.edwards marked this conversation as resolved
@ -0,0 +1217,4 @@
@pytest.mark.asyncio
async def test_on_output_with_normal_content(self, capsys):
"""Test on_output callback with normal content."""
app = ReactiveCleverAgentsApp(unsafe=True)
Member

Lines 1220-1242, lines 1256-1279, and lines 1292-1315 are duplicates, and lines 1338-1354 copies many lines from this set of code. (There are a lot more duplicates in this code.)

Can you turn them into a method?

Lines 1220-1242, lines 1256-1279, and lines 1292-1315 are duplicates, and lines 1338-1354 copies many lines from this set of code. (There are a lot more duplicates in this code.) Can you turn them into a method?
Author
Member

Resolved - created _setup_app_with_callback_capture() helper method

Resolved - created _setup_app_with_callback_capture() helper method
brent.edwards marked this conversation as resolved
@ -0,0 +1367,4 @@
@pytest.mark.asyncio
async def test_on_error_callback(self, capsys):
"""Test on_error callback."""
app = ReactiveCleverAgentsApp(unsafe=True)
Member

Lines 1370-1395 and lines 1408-1433 are duplicates. Could you change this into a method?

Lines 1370-1395 and lines 1408-1433 are duplicates. Could you change this into a method?
Author
Member

Resolved - created _setup_app_with_templates() helper method

Resolved - created _setup_app_with_templates() helper method
brent.edwards marked this conversation as resolved
@ -0,0 +1529,4 @@
app._create_agents()
# Should register llm and tool types
assert mock_factory.register_agent_type.call_count >= 2
Member

Is there a reason we don't know the exact call_count?

Is there a reason we don't know the exact `call_count`?
Author
Member

Changed from >= 2 to == 2

Changed from >= 2 to == 2
brent.edwards marked this conversation as resolved
@ -0,0 +1755,4 @@
@pytest.mark.asyncio
async def test_run_single_shot_with_real_observer(self):
"""Test run_single_shot with actual Observer usage."""
from rx.core import Observer
Member

Observer is never used. You can remove the import.

`Observer` is never used. You can remove the import.
Author
Member

Removed all unused from rx.core import Observer imports

Removed all unused from rx.core import Observer imports
brent.edwards marked this conversation as resolved
@ -0,0 +1878,4 @@
with patch('cleveragents.core.application.ReactiveCleverAgentsApp._setup_routes'):
with patch('cleveragents.core.application.ReactiveCleverAgentsApp._setup_stream_operations'):
with patch('cleveragents.core.application.ReactiveCleverAgentsApp._setup_pipelines'):
app = ReactiveCleverAgentsApp(config_files=[str(temp_path)])
Member

PyCharm warns:

Expected type list[Path] | None, got list[str] instead.

PyCharm warns: Expected type `list[Path] | None`, got `list[str]` instead.
Author
Member

Fixed! Replaced all str(Path(...)) conversions with a get_fixture_path() helper function that returns proper Path objects. All config_files parameters now correctly use list[Path] type. Also created a tests/fixtures/ directory with 10 static YAML files to replace dynamic temp file creation.

Fixed! Replaced all str(Path(...)) conversions with a get_fixture_path() helper function that returns proper Path objects. All config_files parameters now correctly use list[Path] type. Also created a tests/fixtures/ directory with 10 static YAML files to replace dynamic temp file creation.
brent.edwards marked this conversation as resolved
@ -0,0 +2023,4 @@
"[TOOL_EXECUTE:tool2]\n{}\n[/TOOL_EXECUTE]"
)
result = app._process_tool_commands(content)
Member

result is never used.

`result` is never used.
Author
Member

removed unused variable.

removed unused variable.
brent.edwards marked this conversation as resolved
@ -0,0 +2035,4 @@
content = '[TOOL_EXECUTE:test]\n{"param": "value"}\n[/TOOL_EXECUTE]'
result = app._process_tool_commands(content)
Member

result is never used.

`result` is never used.
Author
Member

removed unused variable.

removed unused variable.
brent.edwards marked this conversation as resolved
@ -0,0 +2061,4 @@
assert isinstance(result, str)
class TestVisualizeNetworkExtended:
Member

There is another class named TestVisualizeNetworkExtended on line 2561.

There is another class named `TestVisualizeNetworkExtended` on line 2561.
Author
Member

The duplicate TestVisualizeNetworkExtended class is renamed to TestVisualizeNetworkIntegration in the previous fix. Only one TestVisualizeNetworkExtended exists now.

The duplicate TestVisualizeNetworkExtended class is renamed to TestVisualizeNetworkIntegration in the previous fix. Only one TestVisualizeNetworkExtended exists now.
brent.edwards marked this conversation as resolved
@ -0,0 +2156,4 @@
try:
# Don't mock anything - let it execute
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

PyCharm warns:

Expected type list[Path | None, got list[str] instead.

PyCharm warns: Expected type `list[Path | None`, got `list[str]` instead.
Author
Member

Fixed!

Fixed!
brent.edwards marked this conversation as resolved
@ -0,0 +2186,4 @@
try:
# Load config via app initialization
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

Expected type list[Path | None, got list[str] instead.

Expected type `list[Path | None`, got `list[str]` instead.
Author
Member

Fixed!

Fixed!
brent.edwards marked this conversation as resolved
@ -0,0 +2215,4 @@
app = ReactiveCleverAgentsApp(config_files=[str(temp_path)])
# Create a real-ish response scenario
original_subscribe = app.stream_router.subscribe_to_output
Member

original_subscribe is never used.

`original_subscribe` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +2249,4 @@
agents: {}
"""
with tempfile.NamedTemporaryFile(mode='w', prefix='cleveragent_', suffix='.yaml', delete=False) as f:
Member

Lines 2252-2261, lines 2434-2444, lines 2406-2416, and lines 2462-2472 are all duplicates. Can these be moved to a function?

Lines 2252-2261, lines 2434-2444, lines 2406-2416, and lines 2462-2472 are all duplicates. Can these be moved to a function?
Author
Member

all dynamic file creation has been replaced with static fixture files in tests/fixtures/ using the get_fixture_path() helper function. The duplicate code you mentioned no longer exists.

all dynamic file creation has been replaced with static fixture files in tests/fixtures/ using the get_fixture_path() helper function. The duplicate code you mentioned no longer exists.
brent.edwards marked this conversation as resolved
@ -0,0 +2353,4 @@
temp_path = str(Path(f.name))
try:
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

Expected type list[Path | None, got list[str] instead.

Expected type `list[Path | None`, got `list[str]` instead.
Author
Member

Fixed!

Fixed!
brent.edwards marked this conversation as resolved
@ -0,0 +2379,4 @@
temp_path = str(Path(f.name))
try:
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

Expected type list[Path | None, got list[str] instead.

Expected type `list[Path | None`, got `list[str]` instead.
brent.edwards marked this conversation as resolved
@ -0,0 +2408,4 @@
temp_path = str(Path(f.name))
try:
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

Expected type list[Path | None, got list[str] instead.

Expected type `list[Path | None`, got `list[str]` instead.
brent.edwards marked this conversation as resolved
@ -0,0 +2436,4 @@
temp_path = str(Path(f.name))
try:
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

Expected type list[Path | None, got list[str] instead.

Expected type `list[Path | None`, got `list[str]` instead.
brent.edwards marked this conversation as resolved
@ -0,0 +2464,4 @@
temp_path = str(Path(f.name))
try:
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

Expected type list[Path | None, got list[str] instead.

Expected type `list[Path | None`, got `list[str]` instead.
brent.edwards marked this conversation as resolved
@ -0,0 +2490,4 @@
temp_path = str(Path(f.name))
try:
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

Expected type list[Path | None, got list[str] instead.

Expected type `list[Path | None`, got `list[str]` instead.
brent.edwards marked this conversation as resolved
@ -0,0 +2516,4 @@
temp_path = str(Path(f.name))
try:
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

Expected type list[Path | None, got list[str] instead.

Expected type `list[Path | None`, got `list[str]` instead.
brent.edwards marked this conversation as resolved
@ -0,0 +2544,4 @@
temp_path = str(Path(f.name))
try:
app = ReactiveCleverAgentsApp(config_files=[temp_path])
Member

Expected type list[Path | None, got list[str] instead.

I'm not going to keep pointing these out; there's a lot more of them in this class.

Expected type `list[Path | None`, got `list[str]` instead. I'm not going to keep pointing these out; there's a lot more of them in this class.
brent.edwards marked this conversation as resolved
@ -0,0 +2558,4 @@
Path(temp_path).unlink()
class TestVisualizeNetworkExtended:
Member

There was already a class named TestVisualizeNetworkExtended on line 2064.

There was already a class named `TestVisualizeNetworkExtended` on line 2064.
Author
Member

The duplicate TestVisualizeNetworkExtended class was renamed to TestVisualizeNetworkIntegration in the previous fix. Only one TestVisualizeNetworkExtended exists now.

The duplicate TestVisualizeNetworkExtended class was renamed to TestVisualizeNetworkIntegration in the previous fix. Only one TestVisualizeNetworkExtended exists now.
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-07 00:59:14 +00:00
Dismissed
brent.edwards left a comment
Member

A lot of comments for a complex file.

A lot of comments for a complex file.
brent.edwards requested changes 2025-11-07 01:19:12 +00:00
Dismissed
brent.edwards left a comment
Member

A nice stopping point. Six more comments.

A nice stopping point. Six more comments.
@ -0,0 +8,4 @@
import pytest
import tempfile
from pathlib import Path
from typing import Any, Dict
Member

Neither Any nor Dict is used elsewhere in this code.

Neither `Any` nor `Dict` is used elsewhere in this code.
Author
Member

removed the unused variable.

removed the unused variable.
Member

points to line 12, where both Any and Dict still exist

*points to line 12, where both `Any` and `Dict` still exist*
brent.edwards marked this conversation as resolved
@ -0,0 +26,4 @@
def temp_config_file(self):
"""Create a temporary config file."""
with tempfile.NamedTemporaryFile(mode='w', prefix='cleveragent_', suffix='.yaml', delete=False) as f:
f.write("""
Member

Again, since this file doesn't change, it's better to supply this as a fixed, constant file that's in a directory like tests/repositories. I'm not going to point this out again in this file.

Again, since this file doesn't change, it's better to supply this as a fixed, constant file that's in a directory like `tests/repositories`. I'm not going to point this out again in this file.
Member

This is still here.

This is still here.
brent.edwards marked this conversation as resolved
@ -0,0 +196,4 @@
assert result["nested"]["value"] == "gpt-4-test-nested"
finally:
del os.environ["TEST_MODEL"]
del os.environ["TEST_TEMP"]
Member

What happens if the user had set environment variables TEST_MODEL or TEST_TEMP outside of this test?

What happens if the user had set environment variables `TEST_MODEL` or `TEST_TEMP` outside of this test?
Author
Member

Fixed! The test now saves original environment variable values before setting them, and properly restores them (or deletes if they didn't exist) in the finally block.

Fixed! The test now saves original environment variable values before setting them, and properly restores them (or deletes if they didn't exist) in the finally block.
brent.edwards marked this conversation as resolved
@ -0,0 +286,4 @@
json_str = config_manager.to_json()
assert isinstance(json_str, str)
import json
Member

You usually should put this import at the top of the file.

You usually should put this import at the top of the file.
Author
Member

Moved import json to the top

Moved import json to the top
brent.edwards marked this conversation as resolved
@ -0,0 +83,4 @@
with pytest.raises(ConfigurationError) as exc_info:
raise ConfigurationError("test error")
assert "test error" in str(exc_info.value)
Member

This code is unreaachable, due to line 84.

This code is unreaachable, due to line 84.
Author
Member

The code is reachable and the tests pass successfully. The with pytest.raises() context manager catches the exception, then execution continues after the with block.

The code is reachable and the tests pass successfully. The with pytest.raises() context manager catches the exception, then execution continues after the with block.
brent.edwards marked this conversation as resolved
@ -0,0 +92,4 @@
raise UnsafeConfigurationError("unsafe")
# All should be caught as CleverAgentsException
with pytest.raises(CleverAgentsException):
Member

This line is unreachable, due to line 92.

This line is unreachable, due to line 92.
Author
Member

The code is reachable and the tests pass successfully. The with pytest.raises() context manager catches the exception, then execution continues after the with block.

The code is reachable and the tests pass successfully. The with pytest.raises() context manager catches the exception, then execution continues after the with block.
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-07 01:33:33 +00:00
Dismissed
brent.edwards left a comment
Member

Another good stopping point.

Another good stopping point.
@ -0,0 +35,4 @@
mock_config_class.return_value = mock_config
config_files = [Path("test.yaml")]
network = AgentNetwork(config_files=config_files)
Member

network is never used.

`network` is never used.
Author
Member

Added assert network is not None to verify the network object was successfully created.

Added `assert network is not None` to verify the network object was successfully created.
brent.edwards marked this conversation as resolved
@ -0,0 +64,4 @@
mock_config_class.return_value = mock_config
config_files = [Path("single.yaml")]
network = AgentNetwork(config_files=config_files)
Member

network is never used.

`network` is never used.
Author
Member

Added assert network is not None to verify the network object was successfully created.

Added `assert network is not Non`e to verify the network object was successfully created.
brent.edwards marked this conversation as resolved
@ -0,0 +75,4 @@
mock_config_class.return_value = mock_config
config_files = [Path("config1.yaml"), Path("config2.yaml")]
network = AgentNetwork(config_files=config_files)
Member

network is never used.

`network` is never used.
Author
Member

Added assert network is not None to verify the network object was successfully created.

Added `assert network is not None` to verify the network object was successfully created.
brent.edwards marked this conversation as resolved
@ -0,0 +113,4 @@
network = AgentNetwork()
# Process method is not implemented (functionality moved to ReactiveCleverAgentsApp)
with pytest.raises(NotImplementedError, match="not yet implemented with new reactive system"):
Member

This is a time when reaching 100% code coverage doesn't make sense.

This method is not implemented. Implementing the method would cause the test to fail.

You should let Jeff and Drew know that parts of code aren't implemented, so test coverage shouldn't include them.

(Same comment for lines 125, 133, and 141.)

This is a time when reaching 100% code coverage doesn't make sense. This method is not implemented. Implementing the method would cause the test to fail. You should let Jeff and Drew know that parts of code aren't implemented, so test coverage shouldn't include them. (Same comment for lines 125, 133, and 141.)
Author
Member

Agreed and fixed! Removed all 4 NotImplementedError tests from TestAgentNetworkProcessMethod. Replaced with a comprehensive docstring noting that AgentNetwork.process() is not implemented.

Agreed and fixed! Removed all 4 NotImplementedError tests from TestAgentNetworkProcessMethod. Replaced with a comprehensive docstring noting that AgentNetwork.process() is not implemented.
brent.edwards marked this conversation as resolved
@ -0,0 +175,4 @@
mock_config_class.return_value = mock_config
# Empty list should not trigger load_files
network = AgentNetwork(config_files=[])
Member

network is never used.

`network` is never used.
Author
Member

Added assert network is not None to verify the network object was successfully created.

Added `assert network is not None` to verify the network object was successfully created.
brent.edwards marked this conversation as resolved
@ -0,0 +89,4 @@
assert result == 10
def test_safe_builtins_prevents_dangerous_operations(self):
"""Test that dangerous operations fail with SAFE_BUILTINS."""
Member

The big question: does using SAFE_BUILTINS stop the code from executing, or does it run the code then throw an exception?

You remember what happened when dd if=/dev/zero of=/tmp/file was one of the SAFE_BUILTINS!

You can find this out by, for example, running eval with SAFE_BUILTINS to create a temp file, and seeing whether the file was created (even if there was an error raised).

The big question: does using `SAFE_BUILTINS` stop the code from executing, or does it run the code then throw an exception? You remember what happened when `dd if=/dev/zero of=/tmp/file` was one of the `SAFE_BUILTINS`! You can find this out by, for example, running `eval` with `SAFE_BUILTINS` to create a temp file, and seeing whether the file was created (even if there was an error raised).
Author
Member

@brent.edwards
I have added a critical security test verifying SAFE_BUILTINS prevents execution.

@brent.edwards I have added a critical security test verifying SAFE_BUILTINS prevents execution.
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-08 00:11:13 +00:00
Dismissed
brent.edwards left a comment
Member

You're doing better; thank you for your hard work!

You're doing better; thank you for your hard work!
@ -0,0 +1009,4 @@
import os
try:
if os.path.exists("/tmp/test"):
os.remove("/tmp/test")
Member

If /tmp/test gets created, then the dangerous commands GET EXECUTED. This is a very serious problem!

CleverAgent can do anything that the user can do. Even if CleverAgent isn't run as root, a lot of damage can be done that is listed above:

  • filling the disk
  • downloading and running malware
  • sending all environment variables (including passwords) to another system

and so forth.

Would you like help in fixing this?

If `/tmp/test` gets created, then the dangerous commands GET EXECUTED. This is a very serious problem! CleverAgent can do anything that the user can do. Even if CleverAgent isn't run as root, a lot of damage can be done that is listed above: - filling the disk - downloading and running malware - sending all environment variables (including passwords) to another system and so forth. Would you like help in fixing this? -
Author
Member

Thanks for catching this! We've fixed the test to mock asyncio.create_subprocess_exec so no dangerous commands actually execute - it now safely demonstrates the vulnerability using mocked output. The underlying safe_mode security issue in tool.py still needs addressing; we're open to your suggestions on the best approach (allowlist vs blocklist, etc.). @brent.edwards

Thanks for catching this! We've fixed the test to mock asyncio.create_subprocess_exec so no dangerous commands actually execute - it now safely demonstrates the vulnerability using mocked output. The underlying safe_mode security issue in tool.py still needs addressing; we're open to your suggestions on the best approach (allowlist vs blocklist, etc.). @brent.edwards
brent.edwards marked this conversation as resolved
@ -0,0 +17,4 @@
from cleveragents.core.exceptions import (
UnsafeConfigurationError,
CleverAgentsException,
ConfigurationError
Member

ConfigurationError is never used.

`ConfigurationError` is never used.
Author
Member

removed the unused variables.

removed the unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +33,4 @@
class TestReactiveCleverAgentsAppInitialization:
"""Test suite for ReactiveCleverAgentsApp initialization."""
def test_initialization_without_config(self):
Member

When I run this test, I get the following warning:

tests/unit/core/test_application.py::TestReactiveCleverAgentsAppInitialization::test_initialization_without_config
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/core/application.py:98: DeprecationWarning: There is no current event loop
    self.loop = asyncio.get_event_loop()
When I run this test, I get the following warning: ``` tests/unit/core/test_application.py::TestReactiveCleverAgentsAppInitialization::test_initialization_without_config /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/core/application.py:98: DeprecationWarning: There is no current event loop self.loop = asyncio.get_event_loop() ```
Author
Member

this is fixed already.

this is fixed already.
brent.edwards marked this conversation as resolved
@ -0,0 +985,4 @@
@pytest.fixture
def mock_reactive_config(self):
"""Create a comprehensive mock reactive config."""
from cleveragents.templates.renderer import TemplateEngine
Member

This import is never used.

This import is never used.
Author
Member

removed unused variables.

removed unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +1151,4 @@
with pytest.raises(CleverAgentsException) as exc_info:
await app.run_single_shot("test")
assert "Failed to run" in str(exc_info.value) or "Test error" in str(exc_info.value)
Member

Under what circumstances should you see "Failed to run", and under what circumstances should you see "Test error"?

Should there be two tests?

Or is the output nondeterministic?

Under what circumstances should you see "Failed to run", and under what circumstances should you see "Test error"? Should there be two tests? Or is the output nondeterministic?
Author
Member

Split into two specific tests:
test_run_single_shot_with_generic_exception - verifies generic exceptions get wrapped with "Failed to run" message
test_run_single_shot_with_clever_exception - verifies CleverAgentsException instances are re-raised without wrapping
Each test now has deterministic, precise assertions.

Split into two specific tests: test_run_single_shot_with_generic_exception - verifies generic exceptions get wrapped with "Failed to run" message test_run_single_shot_with_clever_exception - verifies CleverAgentsException instances are re-raised without wrapping Each test now has deterministic, precise assertions.
brent.edwards marked this conversation as resolved
@ -0,0 +1224,4 @@
"""Test on_output callback with normal content."""
from cleveragents.reactive.stream_router import StreamMessage
app, output_callback, _ = await self._setup_app_with_callback_capture()
Member

app is never used; you can replace it with _.

`app` is never used; you can replace it with `_`.
Author
Member

Done!

Done!
brent.edwards marked this conversation as resolved
@ -0,0 +1239,4 @@
"""Test on_output callback with empty content."""
from cleveragents.reactive.stream_router import StreamMessage
app, output_callback, _ = await self._setup_app_with_callback_capture()
Member

app is never used; you can replace it with _.

`app` is never used; you can replace it with `_`.
Author
Member

Done!

Done!
brent.edwards marked this conversation as resolved
@ -0,0 +1247,4 @@
output_callback(test_msg)
captured = capsys.readouterr()
assert "[DEBUG] Empty output received" in captured.out
Member

I just noticed that both you and Jeff use print for debug, warning, and error messages in src/cleveragents/core/application.py .

There are two important reasons to switch these messages to using the logger that you created in line 46 of application.py:

  1. When we're running in the cloud, log messages get copied outside the server and they can be searched.
  2. It's easy to change what level of messages should be reported. For example, most of the time, we won't want the DEBUG messages to be reported.

Could you change application.py to only use print for things that are part of the interactive session?

I just noticed that both you and Jeff use `print` for debug, warning, and error messages in `src/cleveragents/core/application.py` . There are two important reasons to switch these messages to using the `logger` that you created in line 46 of `application.py`: 1. When we're running in the cloud, log messages get copied outside the server and they can be searched. 2. It's easy to change what level of messages should be reported. For example, most of the time, we won't want the `DEBUG` messages to be reported. Could you change `application.py` to only use `print` for things that are part of the interactive session?
Author
Member

replaced all diagnostic print() statements in application.py with appropriate self.logger calls. User facing interactive print() statements remain unchanged.

replaced all diagnostic print() statements in application.py with appropriate self.logger calls. User facing interactive print() statements remain unchanged.
brent.edwards marked this conversation as resolved
@ -0,0 +1254,4 @@
"""Test on_output callback with whitespace-only content."""
from cleveragents.reactive.stream_router import StreamMessage
app, output_callback, _ = await self._setup_app_with_callback_capture()
Member

app is never used; you can replace it with _.

`app` is never used; you can replace it with `_`.
Author
Member

Done!

Done!
brent.edwards marked this conversation as resolved
@ -0,0 +1262,4 @@
output_callback(test_msg)
captured = capsys.readouterr()
assert "[DEBUG] Empty output received" in captured.out
Member

Same comment about [DEBUG].

Same comment about `[DEBUG]`.
brent.edwards marked this conversation as resolved
@ -0,0 +1308,4 @@
"""Test on_error callback."""
from cleveragents.reactive.stream_router import StreamMessage
app, _, error_callback = await self._setup_app_with_callback_capture()
Member

app is never used.

`app` is never used.
Author
Member

Fixed!

Fixed!
brent.edwards marked this conversation as resolved
@ -0,0 +1671,4 @@
@pytest.mark.asyncio
async def test_run_single_shot_with_message_having_no_content(self):
"""Test run_single_shot when message has no content attribute."""
from cleveragents.reactive.stream_router import StreamMessage
Member

This import is never used.

This import is never used.
Author
Member

Removed !

Removed !
brent.edwards marked this conversation as resolved
@ -0,0 +1901,4 @@
app._process_tool_commands(content)
# Should have executed tools
assert app._execute_single_tool.call_count >= 2
Member

Is there a reason this line is still >= 2 instead of knowing the exact value?

(It is fine if a test cannot be more specific because there are nondeterministic parts. But if the code is deterministic, you should use == instead. It will more quickly point out errors.)

Is there a reason this line is still `>= 2` instead of knowing the exact value? (It is ***fine*** if a test cannot be more specific because there are nondeterministic parts. But if the code is deterministic, you should use `==` instead. It will more quickly point out errors.)
Author
Member

replaced >= with ==

replaced >= with ==
brent.edwards marked this conversation as resolved
@ -0,0 +1975,4 @@
captured = capsys.readouterr()
output = captured.out + captured.err
# Should have printed something
assert len(output) > 0
Member

I think this can be a little more specific. (If the /help text is available, it makes sense to use a reference to it.)

I think this can be a little more specific. (If the `/help` text is available, it makes sense to use a reference to it.)
brent.edwards marked this conversation as resolved
@ -0,0 +2050,4 @@
app = ReactiveCleverAgentsApp(config_files=[temp_path])
# Create a real-ish response scenario
original_subscribe = app.stream_router.subscribe_to_output
Member

I don't see original_subscribe being used. Were you planning on doing anything with it?

I don't see `original_subscribe` being used. Were you planning on doing anything with it?
brent.edwards marked this conversation as resolved
@ -0,0 +2082,4 @@
@pytest.mark.asyncio
async def test_run_single_shot_timeout_error(self):
"""Test run_single_shot raises timeout error."""
from cleveragents.reactive.stream_router import StreamMessage
Member

This import is never used.

This import is never used.
Author
Member

Removed !

Removed !
brent.edwards marked this conversation as resolved
@ -0,0 +2291,4 @@
json_str = '{"path": "C:\\\\Users\\\\test"}'
result = ReactiveCleverAgentsApp._sanitize_json_string(json_str)
# Should handle backslashes correctly
assert isinstance(result, str)
Member

Your test should be more specific here.

Your test should be more specific here.
Author
Member

Done!

Done!
brent.edwards marked this conversation as resolved
@ -0,0 +2297,4 @@
"""Test sanitizing JSON with nested quotes."""
json_str = '{"message": "He said \\"hello\\""}'
result = ReactiveCleverAgentsApp._sanitize_json_string(json_str)
assert isinstance(result, str)
Member

Your test should be more specific here.

Your test should be more specific here.
Author
Member

Done!

Done!
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-08 01:28:36 +00:00
Dismissed
brent.edwards left a comment
Member

Request change.

Request change.
@ -0,0 +54,4 @@
def temp_config_file_2(self):
"""Create a second temporary config file for merging tests."""
with tempfile.NamedTemporaryFile(mode='w', prefix='cleveragent_', suffix='.yaml', delete=False) as f:
f.write("""
Member

Another constant file that should be moved to resources.

Another constant file that should be moved to resources.
Author
Member

replaced with the fixture file

replaced with the fixture file
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-11 00:20:28 +00:00
Dismissed
brent.edwards left a comment
Member

Here are the current problems:

Here are the current problems:
@ -0,0 +1013,4 @@
app._setup_pipelines = Mock()
# Should succeed with complete mocking
app.load_configuration([temp_config_file])
Member

Lines 1003-1016 and lines 1025-1038 are identical. They're both just setting up app. You can join them into one function.

Lines 1003-1016 and lines 1025-1038 are identical. They're both just setting up `app`. You can join them into one function.
Author
Member

Extracted duplicate setup code into _setup_mocked_app() helper method

Extracted duplicate setup code into _setup_mocked_app() helper method
brent.edwards marked this conversation as resolved
@ -0,0 +1800,4 @@
app.config = mock_config
# Get agent types before
types_before = set(app.agent_factory.get_agent_types())
Member

types_before is never used.

`types_before` is never used.
Author
Member

removed unused variable

removed unused variable
brent.edwards marked this conversation as resolved
@ -0,0 +6,4 @@
import pytest
import asyncio
from unittest.mock import Mock, patch, AsyncMock, MagicMock
Member

Neither patch nor MagicMock is ever used.

Neither `patch` nor `MagicMock` is ever used.
Author
Member

Removed the unused variables.

Removed the unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +7,4 @@
import pytest
import asyncio
from unittest.mock import Mock, patch, AsyncMock, MagicMock
from pathlib import Path
Member

Path is never used.

`Path` is never used.
Author
Member

Removed the unused variables.

Removed the unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +10,4 @@
from pathlib import Path
from cleveragents.langgraph.bridge import RxPyLangGraphBridge
from cleveragents.langgraph.graph import GraphConfig, LangGraph
Member

GraphConfig is never used.

`GraphConfig` is never used.
Author
Member

Removed the unused variables.

Removed the unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +11,4 @@
from cleveragents.langgraph.bridge import RxPyLangGraphBridge
from cleveragents.langgraph.graph import GraphConfig, LangGraph
from cleveragents.langgraph.nodes import NodeConfig, NodeType, Edge
Member

None of NodeConfig, NodeType nor Edge is used.

None of `NodeConfig`, `NodeType` nor `Edge` is used.
Author
Member

Removed the unused variables.

Removed the unused variables.
brent.edwards marked this conversation as resolved
@ -0,0 +17,4 @@
from rx.scheduler.eventloop import AsyncIOScheduler
@pytest.fixture
Member

When I run tests, I get the following message:

tests/unit/langgraph/test_bridge.py: 54 warnings
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/pytest_asyncio/plugin.py:761: DeprecationWarning: The event_loop fixture provided by pytest-asyncio has been redefined in
  /home/brent.edwards/Workspace/cleveragents-core/tests/unit/langgraph/test_bridge.py:20
  Replacing the event_loop fixture with a custom implementation is deprecated
  and will lead to errors in the future.
  If you want to request an asyncio event loop with a scope other than function
  scope, use the "scope" argument to the asyncio mark when marking the tests.
  If you want to return different types of event loops, use the event_loop_policy
  fixture.
  
    warnings.warn(
When I run tests, I get the following message: ``` tests/unit/langgraph/test_bridge.py: 54 warnings /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/pytest_asyncio/plugin.py:761: DeprecationWarning: The event_loop fixture provided by pytest-asyncio has been redefined in /home/brent.edwards/Workspace/cleveragents-core/tests/unit/langgraph/test_bridge.py:20 Replacing the event_loop fixture with a custom implementation is deprecated and will lead to errors in the future. If you want to request an asyncio event loop with a scope other than function scope, use the "scope" argument to the asyncio mark when marking the tests. If you want to return different types of event loops, use the event_loop_policy fixture. warnings.warn( ```
Author
Member

removed the custom event loop fixture, as pytest-asyncio automatically provides event loop for all async tests.

removed the custom event loop fixture, as pytest-asyncio automatically provides event loop for all async tests.
Member

I still get the following message:

tests/unit/langgraph/test_bridge.py::TestRxPyLangGraphBridgeInit::test_bridge_initialization
  /home/brent.edwards/Workspace/cleveragents-core/tests/unit/langgraph/test_bridge.py:24: DeprecationWarning: There is no current event loop
    return AsyncIOScheduler(loop=asyncio.get_event_loop())
I still get the following message: ``` tests/unit/langgraph/test_bridge.py::TestRxPyLangGraphBridgeInit::test_bridge_initialization /home/brent.edwards/Workspace/cleveragents-core/tests/unit/langgraph/test_bridge.py:24: DeprecationWarning: There is no current event loop return AsyncIOScheduler(loop=asyncio.get_event_loop()) ```
Author
Member

Fixed the deprecation warning by updating the scheduler fixture to handle both async and sync test contexts; replaced the deprecated asyncio.get_event_loop() with asyncio.get_running_loop() ; added a fallback to create a new event loop for synchronous tests ; kept fixtures as regular (non-async) function to maintain compatibility with all the test types.

Fixed the deprecation warning by updating the scheduler fixture to handle both async and sync test contexts; replaced the deprecated asyncio.get_event_loop() with asyncio.get_running_loop() ; added a fallback to create a new event loop for synchronous tests ; kept fixtures as regular (non-async) function to maintain compatibility with all the test types.
brent.edwards marked this conversation as resolved
@ -0,0 +18,4 @@
@pytest.fixture
def event_loop():
Member

It's up to you whether this is important:

This code has both event_loop as a method here and as a parameter in line 30. This would be confusing; the two ideas might not be the same.

Renaming one would make the code clearer.

It's up to you whether this is important: This code has both `event_loop` as a method here and as a parameter in line 30. This would be confusing; the two ideas might not be the same. Renaming one would make the code clearer.
Author
Member

I have now removed the custom event loop fixture, and now the scheduler fixture is using get_event_loop() to get the event loop.

I have now removed the custom event loop fixture, and now the scheduler fixture is using get_event_loop() to get the event loop.
brent.edwards marked this conversation as resolved
@ -0,0 +27,4 @@
@pytest.fixture
def scheduler(event_loop):
Member

It's up to you whether this is important:

This code has both scheduler as a method here and as a parameter in line 36. This would be confusing; the two ideas might not be the same.

Renaming one would make the code clearer.

It's up to you whether this is important: This code has both `scheduler` as a method here and as a parameter in line 36. This would be confusing; the two ideas might not be the same. Renaming one would make the code clearer.
Author
Member

I think this is a valid pytest code, fixtures can depend on other fixtures.

I think this is a valid pytest code, fixtures can depend on other fixtures.
Member

My fault. You are correct.

My fault. You are correct.
brent.edwards marked this conversation as resolved
@ -0,0 +33,4 @@
@pytest.fixture
def stream_router(scheduler):
Member

It's up to you whether this is important:

This code has both stream_router as a method here and as a parameter in lines 42, 78, 86, 411, 429, 469, 498, and 565. This would be confusing; the two ideas might not be the same.

Renaming one would make the code clearer.

I'm not going to go through the other examples of this problem.

And, again -- if you just say "No" to this problem, I won't do anything.

It's up to you whether this is important: This code has both `stream_router` as a method here and as a parameter in lines 42, 78, 86, 411, 429, 469, 498, and 565. This would be confusing; the two ideas might not be the same. Renaming one would make the code clearer. I'm not going to go through the other examples of this problem. And, again -- if you just say "No" to this problem, I won't do anything.
brent.edwards marked this conversation as resolved
@ -0,0 +182,4 @@
def test_create_graph_stream(self, bridge, simple_graph_config):
"""Test creating a stream for graph execution."""
graph = bridge.create_graph_from_config(simple_graph_config)
Member

graph is never used.

`graph` is never used.
Author
Member

removed unused variable

removed unused variable
brent.edwards marked this conversation as resolved
@ -0,0 +232,4 @@
def test_create_state_updater_operator(self, bridge, simple_graph_config):
"""Test creating state updater operator."""
graph = bridge.create_graph_from_config(simple_graph_config)
Member

graph is never used.

`graph` is never used.
Author
Member

removed unused variable

removed unused variable
brent.edwards marked this conversation as resolved
@ -0,0 +237,4 @@
params = {"graph": "test_graph"}
operator = bridge._create_state_updater(params)
assert operator is not None
Member

Is that all that can be said about operator?

Is that all that can be said about `operator`?
Author
Member

added assert callable(operator)

added assert callable(operator)
brent.edwards marked this conversation as resolved
@ -0,0 +250,4 @@
def test_create_state_checkpointer_operator(self, bridge, simple_graph_config):
"""Test creating state checkpointer operator."""
graph = bridge.create_graph_from_config(simple_graph_config)
Member

graph is never used.

`graph` is never used.
Author
Member

removed unused variable

removed unused variable
brent.edwards marked this conversation as resolved
@ -0,0 +255,4 @@
params = {"graph": "test_graph"}
operator = bridge._create_state_checkpointer(params)
assert operator is not None
Member

Is that all that can be said about operator?

Is that all that can be said about `operator`?
Author
Member

added assert callable(operator)

added assert callable(operator)
brent.edwards marked this conversation as resolved
@ -0,0 +268,4 @@
def test_create_node_operator(self, bridge, simple_graph_config):
"""Test creating node operator."""
graph = bridge.create_graph_from_config(simple_graph_config)
Member

graph is never used.

`graph` is never used.
Author
Member

removed unused variable

removed unused variable
brent.edwards marked this conversation as resolved
@ -0,0 +273,4 @@
params = {"graph": "test_graph", "node": "process"}
operator = bridge._create_node_operator(params)
assert operator is not None
Member

Is that all that can be said about operator?

Is that all that can be said about `operator`?
brent.edwards marked this conversation as resolved
@ -0,0 +286,4 @@
def test_create_node_operator_invalid_node(self, bridge, simple_graph_config):
"""Test node operator with invalid node name."""
graph = bridge.create_graph_from_config(simple_graph_config)
Member

graph is never used.

`graph` is never used.
Author
Member

removed unused variable

removed unused variable
brent.edwards marked this conversation as resolved
@ -0,0 +411,4 @@
def test_connect_stream_to_graph(self, bridge, stream_router, simple_graph_config):
"""Test connecting stream to graph."""
# Create graph
graph = bridge.create_graph_from_config(simple_graph_config)
Member

graph is never used.

`graph` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +414,4 @@
graph = bridge.create_graph_from_config(simple_graph_config)
# Create a test stream
from rx.subject import Subject
Member

It's up to you whether imports should move to the top.

It's up to you whether imports should move to the top.
Author
Member

moved to the top.

moved to the top.
brent.edwards marked this conversation as resolved
@ -0,0 +423,4 @@
bridge.connect_stream_to_graph("test_stream", "test_graph")
# Should not raise error
assert True
Member

It's perfectly fine for a test to just make sure that a call doesn't raise error. (Thank you for that comment!) But you don't need assert True at the end.

It's perfectly fine for a test to just make sure that a call doesn't raise error. (Thank you for that comment!) But you don't need `assert True` at the end.
Author
Member

removed assert True

removed assert True
brent.edwards marked this conversation as resolved
@ -0,0 +428,4 @@
@pytest.mark.asyncio
async def test_connect_stream_to_graph_on_message(self, bridge, stream_router, simple_graph_config):
"""Test connect_stream_to_graph on_message callback triggers graph execution."""
from unittest.mock import AsyncMock
Member

AsyncMock was already imported on line 9.

`AsyncMock` was already imported on line 9.
Author
Member

removed unused variable

removed unused variable
brent.edwards marked this conversation as resolved
@ -0,0 +430,4 @@
"""Test connect_stream_to_graph on_message callback triggers graph execution."""
from unittest.mock import AsyncMock
from rx.subject import Subject
from cleveragents.reactive.stream_router import StreamMessage
Member

I think these imports should move to the top. What do you think?

I think these imports should move to the top. What do you think?
Author
Member

moved to top

moved to top
brent.edwards marked this conversation as resolved
@ -0,0 +455,4 @@
# Verify graph.execute was called with the message content
graph.execute.assert_called()
assert graph.execute.call_count >= 1
Member

Can there be an exact comparison using == instead of >=?

Can there be an exact comparison using `==` instead of `>=`?
Author
Member

replace >= with ==

replace >= with ==
brent.edwards marked this conversation as resolved
@ -0,0 +459,4 @@
def test_connect_stream_to_graph_invalid_stream(self, bridge, simple_graph_config):
"""Test connecting non-existent stream raises error."""
graph = bridge.create_graph_from_config(simple_graph_config)
Member

graph is never used.

`graph` is never used.
Author
Member

removed unused variable

removed unused variable
brent.edwards marked this conversation as resolved
@ -0,0 +468,4 @@
def test_connect_stream_to_graph_invalid_graph(self, bridge, stream_router):
"""Test connecting stream to non-existent graph raises error."""
from rx.subject import Subject
Member

Should this import move to the top of the file?

Should this import move to the top of the file?
Author
Member

moved to the top

moved to the top
brent.edwards marked this conversation as resolved
@ -0,0 +480,4 @@
def test_connect_graph_to_stream(self, bridge, simple_graph_config):
"""Test connecting graph to stream."""
graph = bridge.create_graph_from_config(simple_graph_config)
Member

graph is never used.

`graph` is never used.
Author
Member

removed

removed
brent.edwards marked this conversation as resolved
@ -0,0 +497,4 @@
def test_connect_graph_to_existing_stream(self, bridge, stream_router, simple_graph_config):
"""Test connecting graph to existing stream."""
graph = bridge.create_graph_from_config(simple_graph_config)
Member

graph is never used.

`graph` is never used.
Author
Member

removed

removed
brent.edwards marked this conversation as resolved
@ -0,0 +500,4 @@
graph = bridge.create_graph_from_config(simple_graph_config)
# Create existing stream
from rx.subject import Subject
Member

Should this import be at the top of the file?

Should this import be at the top of the file?
Author
Member

moved to the top

moved to the top
brent.edwards marked this conversation as resolved
@ -0,0 +521,4 @@
bridge.create_hybrid_pipeline(config)
# Should complete without error
assert True
Member

Again -- you don't need an assert True at the end.

Again -- you don't need an `assert True` at the end.
Author
Member

removed

removed
brent.edwards marked this conversation as resolved
@ -0,0 +565,4 @@
def test_create_hybrid_pipeline_with_connections(self, bridge, stream_router):
"""Test creating hybrid pipeline with stage connections."""
# Create input stream first
from rx.subject import Subject
Member

Should this import be done just once at the top of the file?

Should this import be done just once at the top of the file?
brent.edwards marked this conversation as resolved
@ -0,0 +678,4 @@
# Execute through RxPy - we need to trigger the async path
# The operator returns an RxPy operator, so we need to apply it
import rx
from rx import operators as ops
Member

Should these imports be at the top? (And neither operators nor ops is ever used.)

Should these imports be at the top? (And neither `operators` nor `ops` is ever used.)
Author
Member

removed unused variables

removed unused variables
brent.edwards marked this conversation as resolved
@ -0,0 +687,4 @@
# Create source and apply operator
source = rx.of(msg)
subscription = source.pipe(operator).subscribe(on_next=on_next)
Member

subscription is never used.

`subscription` is never used.
Author
Member

replaced with _

replaced with _
brent.edwards marked this conversation as resolved
@ -0,0 +693,4 @@
await asyncio.sleep(0.1)
# Check that execution happened
assert len(result_messages) > 0 or graph.execute.called
Member

Lines 668-696 and lines 715-741 are identical; you might be able to unify them.

Lines 668-696 and lines 715-741 are identical; you might be able to unify them.
Member

Is it possible to make this assert statement more precise?

Is it possible to make this `assert` statement more precise?
brent.edwards marked this conversation as resolved
@ -0,0 +723,4 @@
msg = StreamMessage(content="test input", metadata={"key": "value"})
# Execute through RxPy
import rx
Member

Should this be at the top?

Should this be at the top?
Author
Member

moved to the top

moved to the top
brent.edwards marked this conversation as resolved
@ -0,0 +732,4 @@
# Create source and apply operator
source = rx.of(msg)
subscription = source.pipe(operator).subscribe(on_next=on_next)
Member

subscription is never used.

`subscription` is never used.
Author
Member

replaced with _

replaced with _
brent.edwards marked this conversation as resolved
@ -0,0 +738,4 @@
await asyncio.sleep(0.1)
# Check that execution happened and result is from to_dict()
assert len(result_messages) > 0 or graph.execute.called
Member

Can this assert statement be made more precise?

Can this `assert` statement be made more precise?
brent.edwards marked this conversation as resolved
@ -0,0 +758,4 @@
# Test with dict content
msg = StreamMessage(content={"data": "value"}, metadata={"orig": "meta"})
import rx
Member

Should this import be at the top of the file?

Should this import be at the top of the file?
Author
Member

moved to the top

moved to the top
brent.edwards marked this conversation as resolved
@ -0,0 +762,4 @@
result_messages = []
source = rx.of(msg)
subscription = source.pipe(operator).subscribe(on_next=lambda m: result_messages.append(m))
Member

subscription is never used.

`subscription` is never used.
Member

Also, you can simplify the on_next declaration a bit. It doesn't need the lambda; you can just write on_next=result_messages.append.

Also, you can simplify the `on_next` declaration a bit. It doesn't need the `lambda`; you can just write `on_next=result_messages.append`.
Author
Member

replaced with _ and replace lambda with on_next=result_messages.append

replaced with _ and replace lambda with on_next=result_messages.append
brent.edwards marked this conversation as resolved
@ -0,0 +780,4 @@
# Test with string content (should be wrapped)
msg = StreamMessage(content="string data", metadata={})
import rx
Member

Should this import be at the top?

Should this import be at the top?
Author
Member

moved to the top

moved to the top
brent.edwards marked this conversation as resolved
@ -0,0 +784,4 @@
result_messages = []
source = rx.of(msg)
subscription = source.pipe(operator).subscribe(on_next=lambda m: result_messages.append(m))
Member

subscription is never used.

Also, you can simplify the on_next declaration a bit. It doesn't need the lambda; you can just write on_next=result_messages.append .

`subscription` is never used. Also, you can simplify the `on_next` declaration a bit. It doesn't need the `lambda`; you can just write `on_next=result_messages.append` .
Author
Member

subscription is replaced with _ and replaced lambda with on_next=result_messages.append

subscription is replaced with _ and replaced lambda with on_next=result_messages.append
brent.edwards marked this conversation as resolved
@ -0,0 +803,4 @@
msg = StreamMessage(content="test", metadata={})
import rx
Member

¿doʇ ǝɥʇ ʇɐ ǝq ʇɹodɯᴉ sᴉɥʇ plnoɥS

¿doʇ ǝɥʇ ʇɐ ǝq ʇɹodɯᴉ sᴉɥʇ plnoɥS
Author
Member

moved to the top

moved to the top
brent.edwards marked this conversation as resolved
@ -0,0 +807,4 @@
result_messages = []
source = rx.of(msg)
subscription = source.pipe(operator).subscribe(on_next=lambda m: result_messages.append(m))
Member

puǝddɐ˙sǝƃɐssǝɯ‾ʇlnsǝɹ=ʇxǝu‾uo ǝʇᴉɹʍ ʇsnɾ uɐɔ noʎ ;ɐpqɯɐl ǝɥʇ pǝǝu ʇ,usǝop ʇI ˙ʇᴉq ɐ uoᴉʇɐɹɐlɔǝp ʇxǝu‾uo ǝɥʇ ʎɟᴉldɯᴉs uɐɔ noʎ 'osl∀

pǝsn ɹǝʌǝu sᴉ uoᴉʇdᴉɹɔsqns

`puǝddɐ˙sǝƃɐssǝɯ‾ʇlnsǝɹ=ʇxǝu‾uo` ǝʇᴉɹʍ ʇsnɾ uɐɔ noʎ ;ɐpqɯɐl ǝɥʇ pǝǝu ʇ,usǝop ʇI ˙ʇᴉq ɐ uoᴉʇɐɹɐlɔǝp `ʇxǝu‾uo` ǝɥʇ ʎɟᴉldɯᴉs uɐɔ noʎ 'osl∀ pǝsn ɹǝʌǝu sᴉ `uoᴉʇdᴉɹɔsqns`
Author
Member

Fixed!

Fixed!
brent.edwards marked this conversation as resolved
@ -0,0 +837,4 @@
msg = StreamMessage(content="test input", metadata={})
# Execute
import rx
Member

Should this be moved?

Should this be moved?
Author
Member

moved to the top

moved to the top
brent.edwards marked this conversation as resolved
@ -0,0 +841,4 @@
result_messages = []
source = rx.of(msg)
subscription = source.pipe(operator).subscribe(on_next=lambda m: result_messages.append(m))
Member

subscription is never used.

Also, you can simplify the on_next declaration a bit. It doesn't need the lambda; you can just write on_next=result_messages.append .

`subscription` is never used. Also, you can simplify the `on_next` declaration a bit. It doesn't need the `lambda`; you can just write `on_next=result_messages.append` .
Author
Member

fixed!

fixed!
brent.edwards marked this conversation as resolved
@ -0,0 +847,4 @@
await asyncio.sleep(0.1)
# Check execution happened
assert len(result_messages) > 0 or process_node.execute.called
Member

Can this assert statement be made more precise?

Can this `assert` statement be made more precise?
brent.edwards marked this conversation as resolved
@ -0,0 +871,4 @@
msg = StreamMessage(content="test", metadata={"key": "val"})
import rx
Member

copy/paste from above...

copy/paste from above...
Author
Member

moved to the top

moved to the top
brent.edwards marked this conversation as resolved
@ -0,0 +875,4 @@
result_messages = []
source = rx.of(msg)
subscription = source.pipe(operator).subscribe(on_next=lambda m: result_messages.append(m))
Member

copy/paste from above...

copy/paste from above...
Author
Member

replaced with _ and replace lambda with on_next=result_messages.append

replaced with _ and replace lambda with on_next=result_messages.append
brent.edwards marked this conversation as resolved
@ -0,0 +897,4 @@
# Test routing
import rx
from rx import operators as ops
Member

You can move rx to the top; neither ops nor operators are used below.

You can move `rx` to the top; neither `ops` nor `operators` are used below.
Author
Member

moved to the top

moved to the top
brent.edwards marked this conversation as resolved
@ -0,0 +908,4 @@
routed_messages = []
source = rx.from_(messages)
subscription = source.pipe(router_operator).subscribe(on_next=lambda x: routed_messages.append(x))
Member

copy/paste from above.

copy/paste from above.
Author
Member

replaced with _ and replace lambda with on_next=result_messages.append

replaced with _ and replace lambda with on_next=result_messages.append
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-11 01:22:31 +00:00
Dismissed
brent.edwards left a comment
Member

More and more comments. We're getting there.

More and more comments. We're getting there.
@ -0,0 +11,4 @@
from cleveragents.agents.base import Agent
from cleveragents.langgraph.graph import GraphConfig, LangGraph
from cleveragents.langgraph.nodes import Edge, Node, NodeConfig, NodeType
Member

Node is never used.

`Node` is never used.
Author
Member

Removed the unused variables

Removed the unused variables
brent.edwards marked this conversation as resolved
@ -0,0 +143,4 @@
config = GraphConfig(name="test_graph")
graph = LangGraph(config)
assert graph.scheduler is not None
Member

Can anything else be said about graph.scheduler?

(It's okay if nothing else can be said.)

Can anything else be said about `graph.scheduler`? (It's okay if nothing else can be said.)
Author
Member

added more assert statements based on graph instance

added more assert statements based on graph instance
brent.edwards marked this conversation as resolved
@ -0,0 +159,4 @@
config = GraphConfig(name="test_graph")
graph = LangGraph(config)
assert graph.stream_router is not None
Member

Can anything else be said about graph.stream_router?

Can anything else be said about `graph.stream_router`?
Author
Member

added more assert statements based on graph instance.

added more assert statements based on graph instance.
brent.edwards marked this conversation as resolved
@ -0,0 +189,4 @@
]
config = GraphConfig(name="test_graph", nodes=nodes, edges=edges)
graph = LangGraph(config)
Member

Lines 181-192 and lines 200-211 are identical. Maybe they can be unified?

Lines 181-192 and lines 200-211 are identical. Maybe they can be unified?
Author
Member

replaced with a fixture

replaced with a fixture
brent.edwards marked this conversation as resolved
@ -0,0 +226,4 @@
]
config = GraphConfig(name="test_graph", nodes=nodes, edges=edges)
graph = LangGraph(config)
Member

Lines 217-229 and lines 371-383 are identical.

Lines 217-229 and lines 371-383 are identical.
Author
Member

replaced with a fixture

replaced with a fixture
brent.edwards marked this conversation as resolved
@ -0,0 +250,4 @@
Edge(source="start", target="node2"),
Edge(source="node1", target="end"),
Edge(source="node2", target="end"),
]
Member

Lines 242-253, lines 491-502, and lines 1001-1012 are identical.

Lines 242-253, lines 491-502, and lines 1001-1012 are identical.
Author
Member

replaced with a fixture,

replaced with a fixture,
brent.edwards marked this conversation as resolved
@ -0,0 +256,4 @@
graph = LangGraph(config)
# node1 and node2 can execute in parallel
assert len(graph.parallel_groups) >= 0 # May or may not find groups based on implementation
Member

This test will never fail. Is it possible to say anything more?

This test will never fail. Is it possible to say anything more?
Author
Member

Sets parallel=True on node1 and node2 , checks len(graph.parallel_group)>0 (can fail), verifies node1 and node2 are in the same parallel group.

Sets parallel=True on node1 and node2 , checks len(graph.parallel_group)>0 (can fail), verifies node1 and node2 are in the same parallel group.
brent.edwards marked this conversation as resolved
@ -0,0 +281,4 @@
# Should find at least one parallel group with multiple nodes
has_multi_node_group = any(len(group) > 1 for group in graph.parallel_groups)
assert has_multi_node_group or len(graph.parallel_groups) >= 0 # At least check structure
Member

This test will never fail, either. Is it possible to say anything more?

This test will never fail, either. Is it possible to say anything more?
Author
Member

removed or len(..) >=0, checks len(graph.parallel_groups) > 0, checks has_multi_node_group separately (can fail), verifies all three nodes are in the same group.

removed or len(..) >=0, checks len(graph.parallel_groups) > 0, checks has_multi_node_group separately (can fail), verifies all three nodes are in the same group.
brent.edwards marked this conversation as resolved
@ -0,0 +302,4 @@
levels = graph._topological_levels()
assert isinstance(levels, dict)
assert len(levels) > 0
Member

This test can never fail!

This test can never fail!
Author
Member

verifies there are at least 2 levels, verifies "start" is at level 0, verifies "end" is at the last level

verifies there are at least 2 levels, verifies "start" is at level 0, verifies "end" is at the last level
brent.edwards marked this conversation as resolved
@ -0,0 +403,4 @@
edges = [Edge(source="start", target="end")]
config = GraphConfig(name="test_graph", nodes=nodes, edges=edges)
graph = LangGraph(config)
Member

Lines 399-406, lines 943-950, and lines 986-993 are identical.

Lines 399-406, lines 943-950, and lines 986-993 are identical.
Author
Member

created a fixture at the top and utilized the fixture at all three instances

created a fixture at the top and utilized the fixture at all three instances
brent.edwards marked this conversation as resolved
@ -0,0 +420,4 @@
}
result = await graph.execute(input_data)
assert len(result.messages) > 0
Member

This test can never fail, if messages exists and is the right type.

This test can never fail, if `messages` exists and is the right type.
Author
Member

removed redundant len(result.message) > 0 added role check

removed redundant len(result.message) > 0 added role check
brent.edwards marked this conversation as resolved
@ -0,0 +432,4 @@
input_data = {"key": "value"}
result = await graph.execute(input_data)
assert len(result.messages) > 0
Member

This test can never fail, if messages exists and is the right type.

This test can never fail, if `messages` exists and is the right type.
Author
Member

replaced len(..) >0 with exact checks: len == 1, content, and role

replaced len(..) >0 with exact checks: len == 1, content, and role
brent.edwards marked this conversation as resolved
@ -0,0 +483,4 @@
await graph._execute_from_node("start")
# Should have executed at least the start node
assert len(graph.execution_history) >= 0
Member

This test can never fail, if graph.execution_history exists and is of the right type.

This test can never fail, if `graph.execution_history` exists and is of the right type.
Author
Member

changed >=0 to > 0

changed >=0 to > 0
brent.edwards marked this conversation as resolved
@ -0,0 +506,4 @@
await graph._execute_from_node("start")
# Should have executed nodes
assert len(graph.execution_history) >= 0
Member

Lines 490-509 and lines 1000-1019 are identical. Exactly identical tests are a great way to make your line count.

Lines 490-509 and lines 1000-1019 are identical. Exactly identical tests are a great way to make your line count.
Member

In addition, this test can never fail.

In addition, this test can never fail.
Author
Member

removed the exactly identical test, added assert "start" in graph.execution_history (fail)

removed the exactly identical test, added assert "start" in graph.execution_history (fail)
brent.edwards marked this conversation as resolved
@ -0,0 +529,4 @@
edges = [Edge(source="start", target="node1")]
config = GraphConfig(name="test_graph", nodes=nodes, edges=edges)
graph = LangGraph(config)
Member

Lines 525-532, lines 542-549, and 594-601 are identical.

Lines 525-532, lines 542-549, and 594-601 are identical.
Author
Member

These tests use different fixtures (minimal_graph_structure, parallel_graph_structure, and no fixture) and test different execution modes (sequential, parallel, single node). They share a similar pattern but test different behaviors, so unifying them would reduce clarity

These tests use different fixtures (minimal_graph_structure, parallel_graph_structure, and no fixture) and test different execution modes (sequential, parallel, single node). They share a similar pattern but test different behaviors, so unifying them would reduce clarity
Member

Perfect. Then keep them as they are.

Perfect. Then keep them as they are.
brent.edwards marked this conversation as resolved
@ -0,0 +578,4 @@
await graph._execute_nodes_parallel(["start", "node1", "node2"])
# Should have executed all nodes
assert len(graph.execution_history) >= 0
Member

If graph.execution_history exists and is the right type, this test can never fail.

If `graph.execution_history` exists and is the right type, this test can never fail.
Author
Member

Fixed weak assertion assert len(graph.execution_history) >= 0 → now checks exact count (3) and verifies all nodes are in history.

Fixed weak assertion assert len(graph.execution_history) >= 0 → now checks exact count (3) and verifies all nodes are in history.
brent.edwards marked this conversation as resolved
@ -0,0 +618,4 @@
]
config = GraphConfig(name="test_graph", nodes=nodes, edges=edges)
graph = LangGraph(config)
Member

Lines 608-621 and lines 739-752 are identical.

Lines 608-621 and lines 739-752 are identical.
Author
Member

Created conditional_edge_structure fixture to unify identical setup code in test_get_next_nodes_with_condition and test_visualize_with_conditional_edges.

Created conditional_edge_structure fixture to unify identical setup code in test_get_next_nodes_with_condition and test_visualize_with_conditional_edges.
brent.edwards marked this conversation as resolved
@ -0,0 +907,4 @@
await graph._execute_from_node("start")
# Should still complete without hanging
assert not graph.is_running or True # Just ensure it completes
Member

Again, it's PERFECTLY FINE for a test to just say that it's checking that it runs through.

But just let people know; you don't need an always-true assert at the end.

Again, it's PERFECTLY FINE for a test to just say that it's checking that it runs through. But just let people know; you don't need an always-true `assert` at the end.
Author
Member

Removed always-true assertion assert not graph.is_running or True — the test already verifies completion doesn't hang.

Removed always-true assertion assert not graph.is_running or True — the test already verifies completion doesn't hang.
brent.edwards marked this conversation as resolved
@ -0,0 +1016,4 @@
await graph._execute_from_node("start")
# Should have executed nodes
assert len(graph.execution_history) >= 0
Member

If graph.execution_history is of the right type, this test never fails.

If `graph.execution_history` is of the right type, this test never fails.
brent.edwards marked this conversation as resolved
@ -0,0 +1038,4 @@
# Create a mock node type (this shouldn't happen in practice)
# The method should return default shape
from enum import Enum
Member

Should this be at the top of the file?

Should this be at the top of the file?
Author
Member

Fixed — Removed inline from enum import Enum (already imported at top). Updated the test to use Mock (already imported) instead of creating a custom enum

Fixed — Removed inline from enum import Enum (already imported at top). Updated the test to use Mock (already imported) instead of creating a custom enum
brent.edwards marked this conversation as resolved
@ -0,0 +1042,4 @@
class MockType(Enum):
UNKNOWN = "unknown"
shape = graph._get_node_shape(MockType.UNKNOWN)
Member

The method takes a NodeType.

The method takes a `NodeType`.
Author
Member

Replaced the custom MockType(Enum) with Mock(spec=NodeType) since _get_node_shape expects a NodeType. The test now uses a mock to verify the default return value.

Replaced the custom MockType(Enum) with Mock(spec=NodeType) since _get_node_shape expects a NodeType. The test now uses a mock to verify the default return value.
brent.edwards marked this conversation as resolved
@ -0,0 +1228,4 @@
# In an async context, event loop should be running
graph = LangGraph(config)
assert graph.scheduler is not None
Member

Can anything else be said about graph.scheduler?

Can anything else be said about `graph.scheduler`?
Author
Member

Added assert isinstance(graph.scheduler, AsyncIOScheduler) to verify the scheduler type.

Added assert isinstance(graph.scheduler, AsyncIOScheduler) to verify the scheduler type.
brent.edwards marked this conversation as resolved
@ -0,0 +1237,4 @@
# Create graph in sync context
graph = LangGraph(config)
assert graph.scheduler is not None
Member

Can anything else be said about graph.scheduler?

Can anything else be said about `graph.scheduler`?
Author
Member

Added assert isinstance(graph.scheduler, AsyncIOScheduler) to verify the scheduler type.

Added assert isinstance(graph.scheduler, AsyncIOScheduler) to verify the scheduler type.
brent.edwards marked this conversation as resolved
@ -0,0 +1254,4 @@
agents = {}
stream_router = ReactiveStreamRouter()
graph = LangGraph(config, agents, stream_router)
Member

graph is never used.

`graph` is never used.
Author
Member

Removed the unused graph variable

Removed the unused graph variable
brent.edwards marked this conversation as resolved
@ -0,0 +1258,4 @@
# The on_next callback is set up automatically when nodes are reachable
# Verify node stream exists
stream_name = f"__test_graph_node_node1__"
Member

You don't need the f at the start of the string.

You don't need the `f` at the start of the string.
Author
Member

removed the unnecessary f at the start

removed the unnecessary f at the start
brent.edwards marked this conversation as resolved
@ -0,0 +1264,4 @@
@pytest.mark.asyncio
async def test_setup_node_stream_subscriptions_on_error(self):
"""Test _setup_node_stream_subscriptions on_error callback logs errors."""
from rx.subject import Subject
Member

This import is never used.

This import is never used.
Author
Member

removed the unused import

removed the unused import
brent.edwards marked this conversation as resolved
@ -0,0 +1276,4 @@
graph = LangGraph(config, agents, stream_router)
# Get the node stream
stream_name = f"__test_graph_node_node1__"
Member

Lines 1269-1279 and 1299-1309 are identical.

Lines 1269-1279 and 1299-1309 are identical.
Author
Member

replaced the identical code with fixtures

replaced the identical code with fixtures
brent.edwards marked this conversation as resolved
@ -0,0 +1303,4 @@
agents = {}
stream_router = ReactiveStreamRouter()
graph = LangGraph(config, agents, stream_router)
Member

graph is never used.

`graph` is never used.
Author
Member

replaced graph with _

replaced graph with _
brent.edwards marked this conversation as resolved
@ -0,0 +1306,4 @@
graph = LangGraph(config, agents, stream_router)
# Get the node stream
stream_name = f"__test_graph_node_node1__"
Member

You can remove the f from the front of the string.

You can remove the `f` from the front of the string.
Author
Member

removed the f at the start

removed the f at the start
brent.edwards marked this conversation as resolved
@ -0,0 +1316,4 @@
await asyncio.sleep(0.05)
# If we get here without error, the callback works
assert True
Member

Usual comment about it's fine to have tests without assert.

Usual comment about it's fine to have tests without `assert`.
Author
Member

Removed the unnecessary assert True statement.

Removed the unnecessary assert True statement.
brent.edwards marked this conversation as resolved
@ -0,0 +1345,4 @@
graph = LangGraph(config, agents, stream_router)
# Execute the node to trigger async_executor
result = await graph.execute({"messages": [{"role": "user", "content": "test"}]})
Member

result is never used. (You possibly want to use it in an assert statement.)

`result` is never used. (You possibly want to use it in an `assert` statement.)
Author
Member

added assert isinstance(result, GraphState) to verify the return type

added assert isinstance(result, GraphState) to verify the return type
brent.edwards marked this conversation as resolved
@ -0,0 +1353,4 @@
@pytest.mark.asyncio
async def test_register_node_executor_sync_executor(self):
"""Test _register_node_executor sync_executor callback."""
from cleveragents.reactive.stream_router import StreamMessage
Member

Should this be at the top of the page?

Should this be at the top of the page?
Author
Member

moved the inline import to the top

moved the inline import to the top
brent.edwards marked this conversation as resolved
@ -0,0 +1363,4 @@
agents = {}
stream_router = ReactiveStreamRouter()
graph = LangGraph(config, agents, stream_router)
Member

graph is never used.

`graph` is never used.
Author
Member

replaced graph variable with _

replaced graph variable with _
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-11 01:42:56 +00:00
Dismissed
brent.edwards left a comment
Member

A few more.

A few more.
@ -0,0 +3,4 @@
"""
import asyncio
from unittest.mock import AsyncMock, Mock, patch
Member

patch is never used.

`patch` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +235,4 @@
state.messages = [{"role": "user", "content": "Test"}]
state.metadata = {"unsafe_mode": True}
result = await node.execute(state)
Member

result is never used.

`result` is never used.
brent.edwards marked this conversation as resolved
@ -0,0 +255,4 @@
state = GraphState()
state.messages = [{"role": "user", "content": "Test"}]
result = await node.execute(state)
Member

Lines 252-258 and 912-918 are identical.

Lines 252-258 and 912-918 are identical.
Author
Member

Created agent_node_with_state fixture to unify the identical setup code

Created agent_node_with_state fixture to unify the identical setup code
brent.edwards marked this conversation as resolved
@ -0,0 +259,4 @@
# Should return error message in messages
assert "messages" in result
assert "Error processing message" in result["messages"][0]["content"]
Member

Lines 252-262 and lines 709-723 are identical.

Lines 252-262 and lines 709-723 are identical.
Author
Member

test_execute_retry_all_fail now uses the agent_node_with_state fixture, removing duplication.

test_execute_retry_all_fail now uses the agent_node_with_state fixture, removing duplication.
brent.edwards marked this conversation as resolved
@ -0,0 +454,4 @@
result = await node.execute(state)
assert result["metadata"]["condition_result"] is True
Member

Lines 450-457 and 484-491 are identical.

Lines 450-457 and 484-491 are identical.
Author
Member

Created conditional_node_with_two_messages fixture to unify the identical setup

Created conditional_node_with_two_messages fixture to unify the identical setup
brent.edwards marked this conversation as resolved
@ -0,0 +696,4 @@
result = await node.execute(state)
# Retry policy should be attempted
assert node.execution_count >= 1
Member

Is it possible to know the expected execution count?

Is it possible to know the expected execution count?
Author
Member

Changed assert node.execution_count >= 1 to assert node.execution_count >= 2. With max_retries=1, there should be at least 2 executions (1 initial attempt + 1 retry).

Changed assert node.execution_count >= 1 to assert node.execution_count >= 2. With max_retries=1, there should be at least 2 executions (1 initial attempt + 1 retry).
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-11 02:00:39 +00:00
Dismissed
@ -0,0 +119,4 @@
updates = {"messages": [{"role": "assistant", "content": "msg2"}]}
state.update(updates, StateUpdateMode.MERGE)
assert len(state.messages) == 2
Member

Lines 116-122 and lines 138-144 are almost identical.

Lines 116-122 and lines 138-144 are almost identical.
Author
Member

Created state_with_one_message fixture to unify the identical setup code

Created state_with_one_message fixture to unify the identical setup code
brent.edwards marked this conversation as resolved
@ -0,0 +389,4 @@
latest = manager.get_latest_checkpoint()
assert latest is not None
Member

Can anything else be said about latest? If not, that's fine.

Can anything else be said about `latest`? If not, that's fine.
Author
Member

Added assert isinstance(latest, Path) to verify that get_latest_checkpoint() returns a Path instance, not just that it's not None.

Added assert isinstance(latest, Path) to verify that get_latest_checkpoint() returns a Path instance, not just that it's not None.
brent.edwards marked this conversation as resolved
@ -0,0 +422,4 @@
# Time travel goes back in history
# After two updates, history has two snapshots (before each update)
# Going back 1 step means we go to the state before the last update
assert result is not None
Member

Line 426 is strictly stronger than this line.

Line 426 is strictly stronger than this line.
Author
Member

Removed the redundant assert result is not None since assert isinstance(result, GraphState) on line 426 is strictly stronger and already covers the None check

Removed the redundant assert result is not None since assert isinstance(result, GraphState) on line 426 is strictly stronger and already covers the None check
brent.edwards marked this conversation as resolved
@ -0,0 +435,4 @@
result = manager.time_travel(steps_back=10)
# Should go to the oldest available state
assert result is not None
Member

Can anything more be said about result?

Can anything more be said about `result`?
Author
Member

Changed assert result is not None to assert isinstance(result, GraphState) to verify the return type from time_travel().

Changed assert result is not None to assert isinstance(result, GraphState) to verify the return type from time_travel().
brent.edwards marked this conversation as resolved
@ -0,0 +489,4 @@
manager = StateManager()
received_states = []
manager.state_stream.subscribe(lambda state: received_states.append(state))
Member

You don't need the lambda here.

You don't need the `lambda` here.
Author
Member

removed the unnecessary lambda,

removed the unnecessary lambda,
brent.edwards marked this conversation as resolved
@ -0,0 +494,4 @@
manager.reset()
# Should have received the reset state
assert len(received_states) > 0
Member

Can anything more be said about received_states?

Can anything more be said about `received_states`?
Author
Member

added assert isinstance(received_states[0], GraphState)

added assert isinstance(received_states[0], GraphState)
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-11 02:17:52 +00:00
Dismissed
brent.edwards left a comment
Member

More comments...

More comments...
@ -0,0 +5,4 @@
import os
import tempfile
from pathlib import Path
from unittest.mock import Mock, patch
Member

Neither Mock nor patch are used in this file.

Neither `Mock` nor `patch` are used in this file.
Author
Member

removed unused variables

removed unused variables
brent.edwards marked this conversation as resolved
@ -0,0 +138,4 @@
"""Test parser initialization."""
parser = ReactiveConfigParser()
assert parser.logger is not None
Member

Can anything else be written about parser.logger?

Can anything else be written about `parser.logger`?
Author
Member

Added assert isinstance(parser.logger, logging.Logger) to verify the logger type. Moved import logging to the top of the file.

Added assert isinstance(parser.logger, logging.Logger) to verify the logger type. Moved import logging to the top of the file.
brent.edwards marked this conversation as resolved
@ -0,0 +375,4 @@
def test_interpolate_env_vars_missing_no_default_error(self):
"""Test replace_env_var raises error when env var missing and no default."""
from cleveragents.core.exceptions import ConfigurationError
Member

This was already imported in line 13.

This was already imported in line 13.
Author
Member

removed the import

removed the import
brent.edwards marked this conversation as resolved
@ -0,0 +483,4 @@
base = {"key1": "value1", "key2": "value2"}
original_base = base.copy()
parser._merge_configs(base, None)
Member

If you want to allow None for the new parameter of ReactiveConfigParser, then you should change the type in config_parser.py line 120.

If you want to allow `None` for the `new` parameter of `ReactiveConfigParser`, then you should change the type in `config_parser.py` line 120.
Author
Member

Updated the type hint in config_parser.py from new: dict[str, Any] to new: Optional[dict[str, Any]] to match the if new is None: check

Updated the type hint in config_parser.py from new: dict[str, Any] to new: Optional[dict[str, Any]] to match the if new is None: check
brent.edwards marked this conversation as resolved
@ -0,0 +757,4 @@
"""Test validation when route references missing agent."""
parser = ReactiveConfigParser()
from cleveragents.reactive.route import RouteConfig
Member

Do you want to move this to the top of the file?

Do you want to move this to the top of the file?
Author
Member

removed the inline import and moved to top level

removed the inline import and moved to top level
brent.edwards marked this conversation as resolved
@ -0,0 +777,4 @@
"""Test validation with RouteType.STREAM in routes."""
parser = ReactiveConfigParser()
from cleveragents.reactive.route import RouteConfig
Member

Do you want to move this to the top of the file?

Do you want to move this to the top of the file?
Author
Member

removed the inline import and moved to top level

removed the inline import and moved to top level
brent.edwards marked this conversation as resolved
@ -0,0 +803,4 @@
"""Test validation with RouteType.GRAPH in routes."""
parser = ReactiveConfigParser()
from cleveragents.reactive.route import RouteConfig
Member

You only need one of these imports.

You only need one of these imports.
Author
Member

removed the inline import and moved to top level

removed the inline import and moved to top level
brent.edwards marked this conversation as resolved
@ -0,0 +859,4 @@
"""Test validation when config.splits has values."""
parser = ReactiveConfigParser()
from cleveragents.reactive.route import RouteConfig
Member

¿ǝlᴉɟ ǝɥʇ ɟo doʇ ǝɥʇ oʇ sᴉɥʇ ǝʌoɯ oʇ ʇuɐʍ noʎ op

¿ǝlᴉɟ ǝɥʇ ɟo doʇ ǝɥʇ oʇ sᴉɥʇ ǝʌoɯ oʇ ʇuɐʍ noʎ op
Author
Member

removed the inline import and moved to top level

removed the inline import and moved to top level
brent.edwards marked this conversation as resolved
@ -0,0 +890,4 @@
"""Test validation where pipeline has stage_type='graph'."""
parser = ReactiveConfigParser()
from cleveragents.reactive.route import RouteConfig
Member

I'll let you look for the rest of the import statements.

I'll let you look for the rest of the `import` statements.
Author
Member

removed the inline import and moved to top level

removed the inline import and moved to top level
brent.edwards marked this conversation as resolved
brent.edwards requested changes 2025-11-13 03:02:05 +00:00
Dismissed
brent.edwards left a comment
Member

I've resolved the finished problems. Great work!

You have a lot of examples of what I'm looking for. Could you please go over the other files that haven't been explicitly covered and check them for the same things I've been posting? At a quick glance, there are imports that haven't been used in test_stream_router.py and test_route_bridge.py.

Thanks!

I've resolved the finished problems. Great work! You have a lot of examples of what I'm looking for. Could you please go over the other files that haven't been explicitly covered and check them for the same things I've been posting? At a quick glance, there are imports that haven't been used in `test_stream_router.py` and `test_route_bridge.py`. Thanks!
@ -0,0 +694,4 @@
agent.dispose()
class TestStreamableAgentMapOperator:
Member

When I run the tests, I still get the following message:

tests/unit/agents/test_base.py::TestStreamableAgentMapOperator::test_map_operator_transforms_values
  /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/internal/priorityqueue.py:34: RuntimeWarning: coroutine 'Agent._process_wrapper' was never awaited
    def enqueue(self, item: T1) -> None:
  
  Object allocated at:
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/observer.py", line 26
      self._on_next_core(value)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/subject/subject.py", line 62
      observer.on_next(value)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py", line 26
      self._on_next(value)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/operators/map.py", line 37
      result = _mapper(value)
    File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/agents/base.py", line 82
      task = loop.create_task(self._process_wrapper(message_data))

Let me know if you want more lines of the trace.

When I run the tests, I still get the following message: ``` tests/unit/agents/test_base.py::TestStreamableAgentMapOperator::test_map_operator_transforms_values /home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/internal/priorityqueue.py:34: RuntimeWarning: coroutine 'Agent._process_wrapper' was never awaited def enqueue(self, item: T1) -> None: Object allocated at: File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/observer.py", line 26 self._on_next_core(value) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/subject/subject.py", line 62 observer.on_next(value) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/observer/autodetachobserver.py", line 26 self._on_next(value) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/rx/core/operators/map.py", line 37 result = _mapper(value) File "/home/brent.edwards/Workspace/cleverbrag-core/py/.venv/lib/python3.12/site-packages/cleveragents/agents/base.py", line 82 task = loop.create_task(self._process_wrapper(message_data)) ``` Let me know if you want more lines of the trace.
Author
Member

removed the asyncio.new_event_loop() fallback that created an unstarted loop; Now raises RuntimeError if no active loop is found

removed the asyncio.new_event_loop() fallback that created an unstarted loop; Now raises RuntimeError if no active loop is found
brent.edwards marked this conversation as resolved
@ -0,0 +4,4 @@
import asyncio
import tempfile
from enum import Enum
Member

Enum is never used in this code.

`Enum` is never used in this code.
Author
Member

removed unused variables

removed unused variables
brent.edwards marked this conversation as resolved
@ -0,0 +303,4 @@
config = GraphConfig(name="test_graph", nodes=nodes, edges=edges)
graph = LangGraph(config)
Member

Lines 293-305 and lines 458-470 are the same; maybe this graph could be a fixture?

Lines 293-305 and lines 458-470 are the same; maybe this graph could be a fixture?
Author
Member

replaced the duplicate code with fixtures.

replaced the duplicate code with fixtures.
brent.edwards marked this conversation as resolved
@ -0,0 +706,4 @@
result = await node.execute(state)
# Retry policy with max_retries=1 means 1 initial attempt + 1 retry = 2 total
assert node.execution_count >= 2
Member

If you know that the execution_count is two, can you change the test to ==?

If you know that the `execution_count` is two, can you change the test to `==`?
Author
Member

replaced with >=2 with ==2

replaced with >=2 with ==2
Member

This hasn't been replaced.

This hasn't been replaced.
Author
Member

Thanks for pointing that out, I have now replaced it.

Thanks for pointing that out, I have now replaced it.
@ -0,0 +299,4 @@
manager = StateManager()
received_states = []
manager.state_stream.subscribe(lambda state: received_states.append(state))
Member

You don't need the lambda.

You don't need the lambda.
Author
Member

removed the unused lambda

removed the unused lambda
brent.edwards marked this conversation as resolved
@ -0,0 +341,4 @@
assert len(checkpoints) == 1
# Verify checkpoint content
with open(checkpoints[0], 'r') as f:
Member

You usually should include an encoding when you open a file.

You usually should include an `encoding` when you open a file.
Author
Member

added encoding

added encoding
brent.edwards marked this conversation as resolved
brent.edwards left a comment
Member

Thank you so much for your hard work, Aditya.

Thank you so much for your hard work, Aditya.
This pull request has changes conflicting with the target branch.
  • src/cleveragents/agent.py
  • src/cleveragents/agents/base.py
  • src/cleveragents/agents/chain.py
  • src/cleveragents/agents/composite.py
  • src/cleveragents/agents/decorators.py
  • src/cleveragents/agents/factory.py
  • src/cleveragents/agents/llm.py
  • src/cleveragents/agents/tool.py
  • src/cleveragents/core/application.py
  • src/cleveragents/core/config.py
  • src/cleveragents/langgraph/bridge.py
  • src/cleveragents/langgraph/graph.py
  • src/cleveragents/langgraph/nodes.py
  • src/cleveragents/langgraph/state.py
  • src/cleveragents/network.py
  • src/cleveragents/reactive/config_parser.py
  • src/cleveragents/reactive/route.py
  • src/cleveragents/reactive/route_bridge.py
  • src/cleveragents/reactive/stream_router.py
  • src/cleveragents/templates/agent_templates.py
  • src/cleveragents/templates/base.py
  • src/cleveragents/templates/deferred_template.py
  • src/cleveragents/templates/enhanced_registry.py
  • src/cleveragents/templates/graph_templates.py
  • src/cleveragents/templates/inline_jinja_handler.py
  • src/cleveragents/templates/inline_yaml_jinja.py
  • src/cleveragents/templates/jinja_yaml_preprocessor.py
  • src/cleveragents/templates/loaders.py
  • src/cleveragents/templates/registry.py
  • src/cleveragents/templates/renderer.py
  • src/cleveragents/templates/smart_yaml_loader.py
  • src/cleveragents/templates/stream_templates.py
  • src/cleveragents/templates/template_store.py
  • src/cleveragents/templates/yaml_jinja_loader.py
  • src/cleveragents/templates/yaml_preprocessor.py
  • src/cleveragents/templates/yaml_template_engine.py
  • tests/features/environment.py
  • tests/features/steps/application_direct_coverage_steps.py
  • tests/features/steps/application_missing_lines_steps.py
  • tests/features/steps/langgraph_bridge_coverage_steps.py
  • tests/features/steps/reactive_steps.py
  • tests/features/steps/route_bridge_steps.py
  • tests/features/steps/routes_steps.py
  • tests/features/steps/tool_agent_steps.py
  • tests/unit/core/test_json_sanitization.py
  • tests/unit/core/test_tool_command_processing.py
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin tests/unit-tests:tests/unit-tests
git switch tests/unit-tests
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core!22
No description provided.