Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ uv sync
uv sync --extra examples
```

We recommend using the same version of uv as the one used in CI:
```bash
uv self update 0.9.5
```

Using Makefile targets:

Expand Down Expand Up @@ -145,16 +149,3 @@ Run only tests that do NOT require API keys:
```bash
uv run pytest -v -m "not apikey"
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you delete it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion it is a useless section that will be constantly out of date (as it already is - there is no agents dir). I predicted that would happen here #76 (comment).

### Project structure
```
databao/
api.py # public entry: new_agent(...)
core/ # Agent, Pipe, Executor, Visualizer abstractions
agents/ # Lighthouse (default) and React-DuckDB agents
duckdb/ # DuckDB integration and tools
visualizers/ # Vega-Lite chat visualizer and utilities
examples/ # notebooks, demo script, configs
tests/ # pytest suite
```

26 changes: 16 additions & 10 deletions tests/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pytest

import databao
from databao.configs import LLMConfigDirectory


@pytest.fixture
Expand All @@ -20,22 +21,27 @@ def duckdb_conn() -> duckdb.DuckDBPyConnection:
return duckdb.connect("./examples/web_shop_orders/data/web_shop.duckdb")


def _new_agent() -> databao.Agent:
llm_config = LLMConfigDirectory.DEFAULT.model_copy(update={"model_kwargs": {"api_key": "test"}})
return databao.new_agent(llm_config=llm_config)


def test_add_db_with_nonexistent_context_path_raises(duckdb_conn: duckdb.DuckDBPyConnection) -> None:
agent = databao.new_agent()
agent = _new_agent()
with pytest.raises(FileNotFoundError):
agent.add_db(duckdb_conn, context=Path("this_file_does_not_exist_123456.md"))


def test_add_df_with_nonexistent_context_path_raises() -> None:
df = pd.DataFrame({"a": [1, 2, 3]})
agent = databao.new_agent()
agent = _new_agent()
with pytest.raises(FileNotFoundError):
agent.add_df(df, context=Path("another_missing_context_987654.md"))


def test_add_db_with_temp_file_context(temp_context_file: Path, duckdb_conn: duckdb.DuckDBPyConnection) -> None:
"""Test adding a database with context from a temporary file."""
agent = databao.new_agent()
agent = _new_agent()
agent.add_db(duckdb_conn, context=temp_context_file)

assert "db1" in agent.db_context
Expand All @@ -45,7 +51,7 @@ def test_add_db_with_temp_file_context(temp_context_file: Path, duckdb_conn: duc
def test_add_df_with_temp_file_context(temp_context_file: Path) -> None:
"""Test adding a DataFrame with context from a temporary file."""
df = pd.DataFrame({"a": [1, 2, 3]})
agent = databao.new_agent()
agent = _new_agent()
agent.add_df(df, context=temp_context_file)

assert "df1" in agent.df_context
Expand All @@ -54,7 +60,7 @@ def test_add_df_with_temp_file_context(temp_context_file: Path) -> None:

def test_add_db_with_string_context(duckdb_conn: duckdb.DuckDBPyConnection) -> None:
"""Test adding a database with context as a string."""
agent = databao.new_agent()
agent = _new_agent()
context_string = "This is a string context for the database."
agent.add_db(duckdb_conn, context=context_string)

Expand All @@ -65,7 +71,7 @@ def test_add_db_with_string_context(duckdb_conn: duckdb.DuckDBPyConnection) -> N
def test_add_df_with_string_context() -> None:
"""Test adding a DataFrame with context as a string."""
df = pd.DataFrame({"a": [1, 2, 3]})
agent = databao.new_agent(name="ctx_string_agent_df")
agent = _new_agent()
context_string = "This is a string context for the DataFrame."
agent.add_df(df, context=context_string)

Expand All @@ -75,29 +81,29 @@ def test_add_df_with_string_context() -> None:

def test_add_additional_context_with_nonexistent_path_raises() -> None:
"""add_additional_context should raise if given a non-existent Path."""
agent = databao.new_agent()
agent = _new_agent()
with pytest.raises(FileNotFoundError):
agent.add_context(Path("no_such_context_file_123.md"))


def test_add_additional_context_with_temp_file(temp_context_file: Path) -> None:
"""Ensure additional context can be loaded from a temporary file path."""
agent = databao.new_agent()
agent = _new_agent()
agent.add_context(temp_context_file)
assert agent.additional_context == [temp_context_file.read_text()]


def test_add_additional_context_with_string() -> None:
"""Ensure additional context can be provided directly as a string."""
agent = databao.new_agent()
agent = _new_agent()
text = "Global instructions for the agent go here."
agent.add_context(text)
assert agent.additional_context == [text]


def test_add_additional_context_multiple_calls_mixed_sources(temp_context_file: Path) -> None:
"""Calling add_additional_context multiple times should append in order."""
agent = databao.new_agent()
agent = _new_agent()
first = "First global instruction."
second = temp_context_file.read_text()
third = "Third bit of context."
Expand Down
6 changes: 4 additions & 2 deletions tests/test_llms.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def _validate_llm_config(config: LLMConfig) -> None:


@pytest.mark.parametrize("path", example_llm_config_paths, ids=[path.name for path in example_llm_config_paths])
def test_example_llm_configs(path: Path) -> None:
def test_example_llm_configs(path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("OPENAI_API_KEY", "test_key")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember we had a problem with monkeypatch. It overwrited the real API KEY for smoke tests.
Let's think how to keep tests independent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the documentation:

All modifications will be undone after the requesting test function or fixture has finished.

I believe the problem before was with autouse=True in a fixture and not with monkeypatch.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About autouse -

autouse –
If True, the fixture func is activated for all tests that can see it.

I'm not sure it should work for tests in different files, but it was the case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really I can't reproduce this problem with monkeypatch + autouse. Probably I'm missing some details.

Copy link
Contributor Author

@mare5x mare5x Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kosstbarz I figured it out. The problem was in @cached_property in LLMConfig: https://github.com/JetBrains/databao/blob/7fde8ffb8f23aeb2ec47e60c2d4ab1ab1021d985/databao/configs/llm.py#L61
LLMConfig.chat_model was returning the cached LLM from whatever was the first test because the cache is global across all tests.

I will make a PR (#141) to remove @cached_property to avoid similar future problems.

config = LLMConfig.from_yaml(path)
_validate_llm_config(config)

Expand All @@ -39,7 +40,8 @@ def test_example_llm_configs(path: Path) -> None:
LLMConfigDirectory.list_all(),
ids=[c.name for c in LLMConfigDirectory.list_all()],
)
def test_llm_config_directory(config: LLMConfig) -> None:
def test_llm_config_directory(config: LLMConfig, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("OPENAI_API_KEY", "test_key")
_validate_llm_config(config)


Expand Down