Skip to content

Conversation

timfdev
Copy link

@timfdev timfdev commented Aug 12, 2025

pydantic-ai and ag-ui-protocol

need pydantic >= 2.10 and >=2.11.2 respectively, this breaks some of the unit tests

…s allowed token count. Make conflicting libraries pydantic-ai and ag-ui optional; disabling agent route if not installed. Make search routes async and fix small bugs in query building.
Copy link

codspeed-hq bot commented Aug 16, 2025

CodSpeed Performance Report

Merging #1028 will not alter performance

Comparing llm-integration (c35a073) with main (8a2b890)

Summary

✅ 13 untouched

Copy link

codecov bot commented Aug 18, 2025

Codecov Report

❌ Patch coverage is 43.57218% with 1071 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.75%. Comparing base (8a2b890) to head (c35a073).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
orchestrator/search/indexing/indexer.py 22.94% 131 Missing ⚠️
orchestrator/search/retrieval/retriever.py 33.11% 101 Missing ⚠️
orchestrator/search/indexing/traverse.py 32.00% 85 Missing ⚠️
orchestrator/api/api_v1/endpoints/search.py 32.03% 70 Missing ⚠️
orchestrator/cli/speedtest.py 26.74% 62 Missing and 1 partial ⚠️
orchestrator/search/filters/base.py 42.05% 62 Missing ⚠️
orchestrator/cli/resize_embedding.py 21.21% 51 Missing and 1 partial ⚠️
orchestrator/search/retrieval/utils.py 22.72% 51 Missing ⚠️
orchestrator/search/core/types.py 65.03% 50 Missing ⚠️
orchestrator/search/retrieval/validation.py 25.00% 45 Missing ⚠️
... and 18 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1028      +/-   ##
==========================================
- Coverage   85.14%   78.75%   -6.40%     
==========================================
  Files         217      251      +34     
  Lines       10495    12387    +1892     
  Branches     1004     1214     +210     
==========================================
+ Hits         8936     9755     +819     
- Misses       1305     2372    +1067     
- Partials      254      260       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +46 to +51
dotenv run python main.py search semantic "Shop for an alligator store"
...
{
"path": "subscription.shop.shop_description",
"value": "Kingswood reptiles shop"
},

Choose a reason for hiding this comment

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

We should make the examples more generic (also the ones below), since this is specific for the WFO instance where we built the initial POC.

FilterCondition = (
DateFilter # DATETIME
| NumericFilter # INT/FLOAT
| StringFilter # STRING TODO: convert to hybrid search

Choose a reason for hiding this comment

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

do we need to make a ticket for this TODO?

Copy link
Author

Choose a reason for hiding this comment

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

Im thinking that maybe this stringfilter should be removed altogether, its already possible to do a hybrid search by passing a user query, passing something like the top 5 results back to the agent will probably yield better results.

For things like booleans/product blocks , we already have the equality filter. Matching on exact text by letting the agent fill in a string will probably not work well.

except Exception as e:
logger.warning(f"Failed to load schema for prompt: {e}")
schema_info = " Schema temporarily unavailable"
logger.error(f"Generated schema for agent prompt:\n{schema_info}")
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is suppose to be an error log?

# We are explicitly excluding 'traceback' and 'steps'
# to avoid overloading the index with too much data.
_process_fields_to_exclude: set[str] = {
"traceback",
Copy link
Contributor

Choose a reason for hiding this comment

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

only excluding traceback? update exclude list or the above comment

@@ -92,6 +92,34 @@ class AppSettings(BaseSettings):
EXPOSE_SETTINGS: bool = False
EXPOSE_OAUTH_SETTINGS: bool = False

# Pydantic-ai Agent settings
AGENT_MODEL: str = "openai:gpt-4o-mini" # See pydantic-ai docs for supported models.
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be nice to create a different settings class for LLM settings

def _extract_matching_field_from_filters(filters: FilterTree) -> MatchingField | None:
"""Extract the first path filter to use as matching field for structured searches.

TODO: Should we allow a list of matched fields in the MatchingField model?
Copy link
Contributor

Choose a reason for hiding this comment

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

what to do with this? new issue?

@Mark90 Mark90 self-requested a review September 16, 2025 12:52
Copy link
Contributor

@Mark90 Mark90 left a comment

Choose a reason for hiding this comment

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

Nice, that's a lot of work 🔥

Overall structure of the code is good, that's why I was able to leave a lot of questions and small remarks. I mean this as a good thing :)

@@ -18,7 +18,7 @@ jobs:
options: --privileged
services:
postgres:
image: postgres:15-alpine
image: pgvector/pgvector:pg15
Copy link
Contributor

Choose a reason for hiding this comment

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

What does this add on top of the normal postgres image? Is there documentation for the extra postgres configuration/extensions required?

search_params=search_params,
db_session=db.session,
pagination_params=pagination_params,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Have there been any thoughts about implementing access control?

Choose a reason for hiding this comment

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

This is planned for later.



def build_agent_app() -> ASGIApp:
if not app_settings.AGENT_MODEL or not app_settings.OPENAI_API_KEY:
Copy link
Contributor

Choose a reason for hiding this comment

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

These settings are strings that can't be None so by default it will be enabled. Since users need to configure the LLM setup, by default it should IMO be disabled with a bool variable like AGENT_ENABLED


return agent.to_ag_ui(deps=StateDeps(SearchState()))
except Exception as e:
logger.error("Agent init failed; serving disabled stub.", error=str(e))
Copy link
Contributor

Choose a reason for hiding this comment

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

What kind of failures has this shown?

sa.Column("entity_id", postgresql.UUID, nullable=False),
sa.Column("path", LtreeType, nullable=False),
sa.Column("value", sa.Text, nullable=False),
sa.Column("embedding", Vector(TARGET_DIM), nullable=True),
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this require the database to have pgvector installed?

entity_scores.join(entity_highlights, entity_scores.c.entity_id == entity_highlights.c.entity_id)
)
).cte("ranked_results")

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we split this function up in one for the DB interaction part which produces an output, and another function that performs the below computations based on the former's output? And preferably also some unittests for the latter

@@ -0,0 +1,447 @@
from abc import ABC, abstractmethod
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe split up into a package with a module for each retriever type, it's a lot of scrolling now :)


def _quantize_score_for_pagination(self, score_value: float) -> BindParameter[Decimal]:
"""Convert score value to properly quantized Decimal parameter for pagination."""
pas_dec = Decimal(str(score_value)).quantize(Decimal("0.000000000001"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this change along with the SCORE_PRECISION if that ever changes?

If so maybe do something like f'{1 / 10**precision:.{precision}f}


if not matches:
substring_pattern = re.escape(word)
matches = list(re.finditer(substring_pattern, text, re.IGNORECASE))
Copy link
Contributor

Choose a reason for hiding this comment

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

If a resulting text has both word and substring matches, wouldn't we want to highlight the substring matches as well?


class TypeDefinition(BaseModel):
operators: list[FilterOp]
valueSchema: dict[FilterOp, ValueSchema]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is camelCase needed here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants