Repository: truera/trulens
Issue: [BUG] Feedback failing to be added in TruApp (LLM as a judge)
Root cause: Validation Gap

**Bug Description**
I want to build an LLMOps pipeline for Cortex Agents (I have just configured an Agent that combines two Cortex Analysts semantic views). However, I'm facing an error when  I want to add feedbacks that are evaluating the results using the LLM as a judge approach.

**To Reproduce**
from trulens.otel.semconv.trace import SpanAttributes
from trulens.core.otel.instrument import instrument
from trulens.core import TruSession
from trulens.connectors.snowflake import SnowflakeConnector
from trulens.apps.app import TruApp
from trulens.core.run import RunConfig

from snowflake.snowpark import Session
from snowflake.core import Root
from pydantic import BaseModel, PrivateAttr
from snowflake.core.cortex.lite_agent_service import AgentRunRequest
from typing import Any, Type
import json
import logging

from trulens.providers.cortex.provider import Cortex
from trulens.core import Feedback, SnowflakeFeedback, Select
from trulens.core.feedback import feedback as core_feedback

session = Session.builder.configs(connection_params).create()
os.environ["TRULENS_OTEL_TRACING"] = "1"
sf_connector = SnowflakeConnector(snowpark_session=session)
tru_session = TruSession(sf_connector)

# ---- Agent Setup ----
class CortexAgentArgs(BaseModel):
    query: str

class CortexAgentTool:
    name: str = "CortexAgent"
    description: str = ""
    args_schema: Type[CortexAgentArgs] = CortexAgentArgs

    _session: Session = PrivateAttr()
    _root: Root = PrivateAttr()
    _agent_service: Any = PrivateAttr()

    def __init__(self, session: Session):
        self._session = session
        self._root = Root(session)
        self._agent_service = self._root.cortex_agent_service

    def _build_request(self, query: str) -> AgentRunRequest:
        return AgentRunRequest.from_dict({
            "model": "claude-sonnet-4-5",
            "type": "auto",
            "tools": [
                {"tool_spec": {"type": "cortex_analyst_text_to_sql", "name": "CM_ANALYST_AGENT"}},
                {"tool_spec": {"type": "cortex_analyst_text_to_sql", "name": "DBO_HISTORY_ANALYST_AGENT"}},
            ],
            "tool_resources": {
                "CM_ANALYST_AGENT": {
                    "semantic_view": "PDM_MM_DB.SEMANTICS.CM_SEMANTIC_VIEW",
                    "execution_environment": {
                            "type": "warehouse",
                            "warehouse": "PDM_MM_WH",
                            "query_timeout": 60
                        }
                    },
                "DBO_HISTORY_ANALYST_AGENT": {
                    "semantic_view": "PDM_MM_DB.SEMANTICS.DBO_HISTORY_SEMANTIC_VIEW",
                    "execution_environment": {
                            "type": "warehouse",
                            "warehouse": "PDM_MM_WH",
                            "query_timeout": 60
                        }
                    },
                
            },
            "messages": [
                {"role": "user", "content": [{"type": "text", "text": query}]}
            ]
        })

    def _consume_stream(self, stream):
        text, sql, citations = "", "", []
        for evt in stream.events():
            try:
                delta = (evt.data.get("delta") if isinstance(evt.data, dict)
                         else json.loads(evt.data).get("delta")
                         or json.loads(evt.data).get("data", {}).get("delta"))
            except Exception:
                continue

            if not isinstance(delta, dict):
                continue

            logger.debug(f"Delta received: {delta}")

            for item in delta.get("content", []):
                if item.get("type") == "text":
                    text += item.get("text", "")
                elif item.get("type") == "tool_results":
                    for result in item["tool_results"].get("content", []):
                        if result.get("type") != "json":
                            continue
                        j = result["json"]
                        text += j.get("text", "")
                        sql = j.get("sql", sql)
                        citations.extend({
                            "source_id": s.get("source_id"),
                            "doc_id": s.get("doc_id")
                        } for s in j.get("searchResults", []))
        return text, sql, str(citations)

    @instrument(
        span_type=SpanAttributes.SpanType.RECORD_ROOT,
        attributes={
            SpanAttributes.RECORD_ROOT.INPUT: "query",
            SpanAttributes.RECORD_ROOT.OUTPUT: "return",
        },
    )
    def run(self, query: str, **kwargs):
        """
        This agent will retrieve PDM data from Snowflake using Text2SQL.
        """
        req = self._build_request(query)
        stream = self._agent_service.run(req)
        text, sql, citations = self._consume_stream(stream)

        results_str = ""
        if sql:
            try:
                df = self._session.sql(sql.rstrip(";")).to_pandas()
                results_str = df.to_string(index=False)
            except Exception as e:
                results_str = f"SQL execution error: {e}"

        return text, citations, sql, results_str

cortex_agent_tool = CortexAgentTool(session=session)
provider = Cortex(session, "mistral-large2")

# SQL Relevance - How relevant is the generated SQL to the users prompt?
sql_gen_criteria = """Provided is an interpretation of a user's query as input 1 and an LLM generated SQL query 
                    designed to answer the users query as input 2. Grade how relevant the SQL code 
                    appears to be to the user's question."""

sql_relevance = (
            Feedback(provider.relevance_with_cot_reasons, 
            name = "SQL relevance")    
            .on_input_output())

feedback_list = [sql_relevance]
tru_app = TruApp(
    app=cortex_agent_tool,
    app_name="cortex_analyst",
    app_version="analyst_only",
    main_method=cortex_agent_tool.run,
    feedbacks=feedback_list,
)


**Expected Behavior**
A clear and concise description of what you expected to happen.

**Relevant Logs/Tracebacks**
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[26], line 1
----> 1 tru_app = TruApp(
      2     app=cortex_agent_tool,
      3     app_name="cortex_analyst",
      4     app_version="analyst_only",
      5     main_method=cortex_agent_tool.run,
      6     feedbacks=feedback_list,
      7 )

File ~/Projects/SFL/MM/cortex-agents-llmops/.venv/lib/python3.12/site-packages/trulens/apps/app.py:387, in TruApp.__init__(self, app, main_method, methods_to_instrument, **kwargs)
    384 kwargs["instrument"] = instrument
    386 # This does instrumentation:
--> 387 super().__init__(**kwargs)
    389 methods_to_instrument = methods_to_instrument or dict()
    391 # The rest of this code instruments methods explicitly passed to
    392 # constructor as needing instrumentation and checks that methods
    393 # decorated with @instrument or passed explicitly belong to some
    394 # component as per serialized version of this app. If they are not,
    395 # placeholders are made in `app_extra_json` so that subsequent
    396 # serialization looks like the components exist.

File ~/Projects/SFL/MM/cortex-agents-llmops/.venv/lib/python3.12/site-packages/trulens/core/app.py:615, in App.__init__(self, connector, feedbacks, **kwargs)
    612 if otel_enabled and main_method is not None:
    613     self._wrap_main_function(app, main_method.__name__)
--> 615 self._tru_post_init()

File ~/Projects/SFL/MM/cortex-agents-llmops/.venv/lib/python3.12/site-packages/trulens/core/app.py:887, in App._tru_post_init(self)
    885 else:
    886     if len(self.feedbacks) > 0:
--> 887         raise ValueError(
    888             "Feedback logging requires `App.connector` to be specified."
    889         )
    891 for f in self.feedbacks:
    892     if (
    893         self.feedback_mode == feedback_schema.FeedbackMode.DEFERRED
    894         or f.run_location
    895         == feedback_schema.FeedbackRunLocation.SNOWFLAKE
    896     ):

ValueError: Feedback logging requires `App.connector` to be specified.

**Environment:**

- OS: MacOS M3
- Python 3.12.11
- TruLens 2.5.1
- Versions of other relevant installed libraries

**Additional Context**
Add any other context about the problem here.


<!-- Greeting -->
Hi @jorisrombouts! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the trulens team.

<!-- Answer -->
The error happens because TruApp requires a connector to be specified when you add feedbacks—this tells TruLens where to log feedback data. In your setup, you created a SnowflakeConnector (`sf_connector`) and a TruSession, but you didn't pass `sf_connector` to TruApp. To fix this, just add the connector argument:

```python
tru_app = TruApp(
    app=cortex_agent_tool,
    app_name="cortex_analyst",
    app_version="analyst_only",
    main_method=cortex_agent_tool.run,
    feedbacks=feedback_list,
    connector=sf_connector  # <-- add this line
)
```

For best performance with Snowflake, use `SnowflakeFeedback` instead of the generic `Feedback` class for your feedbacks, and make sure your SnowflakeConnector is initialized with `init_server_side=True` if you want server-side feedback computation. Example:

```python
sql_relevance = (
    SnowflakeFeedback(
        provider.relevance_with_cot_reasons,
        name="SQL relevance"
    ).on_input_output()
)
```

This approach is confirmed in [community discussions and usage examples](https://github.com/truera/trulens/discussions/2060) and [Snowflake-specific feedback logging guidance](https://github.com/truera/trulens/issues/1717).


<!-- Dosu Comment Footer -->
*To reply, just mention [@dosu](https://go.dosu.dev/dosubot).*

---
<sup>How did I do? [Good](https://app.dosu.dev/response-feedback/47b4ae2e-845e-40f1-9ad1-914929629255?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/47b4ae2e-845e-40f1-9ad1-914929629255?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/47b4ae2e-845e-40f1-9ad1-914929629255?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/47b4ae2e-845e-40f1-9ad1-914929629255?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/47b4ae2e-845e-40f1-9ad1-914929629255?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/47b4ae2e-845e-40f1-9ad1-914929629255?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/47b4ae2e-845e-40f1-9ad1-914929629255?feedback_type=other)</sup>

[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/e16d1378-a540-46b6-bd0e-816c493d2aee/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=The%20error%20happens%20because%20TruApp%20requires%20a%20connector%20to%20be%20specified%20when%20you%20add%20feedbacks%E2%80%94this%20tells%20TruLens%20where%20to%20log%20feedback%20data.%20In%20your%20setup%2C%20you%20created%20a%20SnowflakeConnector%20%28%60sf_connector%60%29%20and%20a%20TruSession%2C%20but%20you%20didn%27t%20pass%20%60sf_connector%60%20to%20TruApp.%20To%20fix%20this%2C%20just%20add%20the%20connector%20argument%3A%0A%0A%60%60%60python%0Atru_app%20%3D%20TruApp%28%0A%20%20%20%20app%3Dcortex_agent_tool%2C%0A%20%20%20%20app_name%3D%22cortex_analyst%22%2C%0A%20%20%20%20app_version%3D%22analyst_only%22%2C%0A%20%20%20%20main_method%3Dcortex_agent_tool.run%2C%0A%20%20%20%20feedbacks%3Dfeedback_list%2C%0A%20%20%20%20connector%3Dsf_connector%20%20%23%20%3C--%20add%20this%20line%0A%29%0A%60%60%60%0A%0AFor%20best%20performance%20with%20Snowflake%2C%20use%20%60SnowflakeFeedback%60%20instead%20of%20the%20generic%20%60Feedback%60%20class%20for%20your%20feedbacks%2C%20and%20make%20sure%20your%20SnowflakeConnector%20is%20initialized%20with%20%60init_server_side%3DTrue%60%20if%20you%20want%20server-side%20feedback%20computation.%20Example%3A%0A%0A%60%60%60python%0Asql_relevance%20%3D%20%28%0A%20%20%20%20SnowflakeFeedback%28%0A%20%20%20%20%20%20%20%20provider.relevance_with_cot_reasons%2C%0A%20%20%20%20%20%20%20%20name%3D%22SQL%20relevance%22%0A%20%20%20%20%29.on_input_output%28%29%0A%29%0A%60%60%60%0A%0AThis%20approach%20is%20confirmed%20in%20%5Bcommunity%20discussions%20and%20usage%20examples%5D%28https%3A//github.com/truera/trulens/discussions/2060%29%20and%20%5BSnowflake-specific%20feedback%20logging%20guidance%5D%28https%3A//github.com/truera/trulens/issues/1717%29.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/truera/trulens/issues/2301)

@dosu

I added the connector, however the error stays the same.

tru_app = TruApp(
    app=cortex_agent_tool,
    app_name="cortex_analyst",
    app_version="analyst_only",
    main_method=cortex_agent_tool.run,
    feedbacks=feedback_list,
    connector=sf_connector
)

or shouldn't I add feedbacks here in the app? But where else?

Furthermore, I see in the documentation that SnowflakeFeedback is deprecated.

"""[DEPRECATED] Similar to the parent class Feedback except this ensures the feedback is run only on the Snowflake server.

    This class is deprecated and will be removed in the next major release. Please use Feedback or [Snowflake AI Observability](https://docs.snowflake.com/en/user-guide/snowflake-cortex/ai-observability/evaluate-ai-applications) instead.
    """

<!-- Answer -->
You should continue adding feedbacks at TruApp initialization—this is the standard and expected usage. The persistent ValueError, even after adding the connector, often points to a configuration or compatibility issue. Here are the most common causes and solutions:

1. Make sure your SnowflakeConnector is initialized with init_server_side=True if you want server-side feedback computation. For example:
```python
sf_connector = SnowflakeConnector(snowpark_session=session, init_server_side=True)
```
2. Double-check that the connector instance you pass to TruApp is the exact one used to create your TruSession and is not None or misconfigured.

3. There are known compatibility issues with certain Snowflake-related libraries. If you use snowflake-sqlalchemy, try downgrading to version 1.7.1, as this has resolved similar issues for others [see discussion](https://github.com/truera/trulens/issues/1717).

4. SnowflakeFeedback is deprecated; use Feedback or Snowflake AI Observability instead. Your current approach of using Feedback is correct for new code.

5. If you want feedback computation to complete before the app returns a response, set feedback_mode="with_app" in TruApp.

If you still see the error after these checks, it likely means the connector is not being recognized by TruApp due to a subtle config or version mismatch [see discussion](https://github.com/truera/trulens/discussions/2060). Double-check all connector-related code and library versions.


<!-- Dosu Comment Footer -->
*To reply, just mention [@dosu](https://go.dosu.dev/dosubot).*

---
<sup>How did I do? [Good](https://app.dosu.dev/response-feedback/7eee7398-23f1-4a73-999a-d841c0bb8352?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/7eee7398-23f1-4a73-999a-d841c0bb8352?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/7eee7398-23f1-4a73-999a-d841c0bb8352?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/7eee7398-23f1-4a73-999a-d841c0bb8352?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/7eee7398-23f1-4a73-999a-d841c0bb8352?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/7eee7398-23f1-4a73-999a-d841c0bb8352?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/7eee7398-23f1-4a73-999a-d841c0bb8352?feedback_type=other)</sup>

[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/e16d1378-a540-46b6-bd0e-816c493d2aee/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=You%20should%20continue%20adding%20feedbacks%20at%20TruApp%20initialization%E2%80%94this%20is%20the%20standard%20and%20expected%20usage.%20The%20persistent%20ValueError%2C%20even%20after%20adding%20the%20connector%2C%20often%20points%20to%20a%20configuration%20or%20compatibility%20issue.%20Here%20are%20the%20most%20common%20causes%20and%20solutions%3A%0A%0A1.%20Make%20sure%20your%20SnowflakeConnector%20is%20initialized%20with%20init_server_side%3DTrue%20if%20you%20want%20server-side%20feedback%20computation.%20For%20example%3A%0A%60%60%60python%0Asf_connector%20%3D%20SnowflakeConnector%28snowpark_session%3Dsession%2C%20init_server_side%3DTrue%29%0A%60%60%60%0A2.%20Double-check%20that%20the%20connector%20instance%20you%20pass%20to%20TruApp%20is%20the%20exact%20one%20used%20to%20create%20your%20TruSession%20and%20is%20not%20None%20or%20misconfigured.%0A%0A3.%20There%20are%20known%20compatibility%20issues%20with%20certain%20Snowflake-related%20libraries.%20If%20you%20use%20snowflake-sqlalchemy%2C%20try%20downgrading%20to%20version%201.7.1%2C%20as%20this%20has%20resolved%20similar%20issues%20for%20others%C2%A0%5Bsee%20discussion%5D%28https%3A//github.com/truera/trulens/issues/1717%29.%0A%0A4.%20SnowflakeFeedback%20is%20deprecated%3B%20use%20Feedback%20or%20Snowflake%20AI%20Observability%20instead.%20Your%20current%20approach%20of%20using%20Feedback%20is%20correct%20for%20new%20code.%0A%0A5.%20If%20you%20want%20feedback%20computation%20to%20complete%20before%20the%20app%20returns%20a%20response%2C%20set%20feedback_mode%3D%22with_app%22%20in%20TruApp.%0A%0AIf%20you%20still%20see%20the%20error%20after%20these%20checks%2C%20it%20likely%20means%20the%20connector%20is%20not%20being%20recognized%20by%20TruApp%20due%20to%20a%20subtle%20config%20or%20version%20mismatch%C2%A0%5Bsee%20discussion%5D%28https%3A//github.com/truera/trulens/discussions/2060%29.%20Double-check%20all%20connector-related%20code%20and%20library%20versions.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/truera/trulens/issues/2301)

Hi @dosu, I'm still getting the same error.