Repository: truera/trulens
Issue: Issue: TruSession and get_records_and_feedback() displaying results before feedback function results are populated
Root cause: Resource Mishandling

**Bug Description**
After using TruChain and LLMChain to complete my LLM pipeline, I'm trying to access feedback functions from the generated output. After I first run the command to generate a LLM response, the feedback function columns haven't 1) been created in the datatrame 2) been populated in the dataframe. 

When I run it a second time with the same command, the feedback function columns have appeared and are now populated from the previous run, but are now NAN for the current run. This delay in populating the feedback continues as I continue to generate new responses. 

**To Reproduce**
Assuming llm_chain is a fully functional LLMChain:

from trulens.providers.huggingface import Huggingface
from trulens.core import Feedback
from trulens.core import TruSession
from trulens.apps.langchain import TruChain
from trulens.dashboard import run_dashboard

session = TruSession()

# Define a pii_detection feedback function using HuggingFace.
f_pii_detection = Feedback(hugs.pii_detection_with_cot_reasons).on_input()

tru_recorder = TruChain(
    llm_chain, app_name="Chain1_ChatApplication", feedbacks=[f_pii_detection]
)

with tru_recorder as recording:
    prompt_input = "What is the typical weather in Ireland?"
    llm_response = llm_chain(prompt_input)
    
dataframe, feedback = session.get_records_and_feedback()
    
for i in range(len(dataframe)):
    print(dataframe.iloc[i])


**Environment:**

- Python==3.10
- TruLens==1.0.2
- Langchain== 0.3.0


<!-- Greeting -->
Hey there, @gleeson-analytics! 👋 I'm here to help you with your TruLens issue. I can assist with bug fixes, answer your questions, and even help you become a contributor.

<!-- Answer -->
To ensure that the feedback function columns in `TruSession` and `get_records_and_feedback()` are created and populated correctly on the first run when using `TruChain` and `LLMChain`, follow these steps:

1. **Create Feedback Functions**: Define the feedback functions you want to use.
2. **Initialize `TruSession` and `TruChain`**: Set up the session and chain.
3. **Run Feedback Functions**: Execute the feedback functions and ensure they are added to the database.
4. **Verify Results**: Retrieve and verify the feedback results from the database.

Here is a code snippet that demonstrates these steps:

```python
from trulens.core import Feedback, TruSession
from trulens.apps.langchain import TruChain
from langchain.prompts import PromptTemplate
from langchain.schema import StrOutputParser
from langchain_openai import OpenAI
from trulens.providers.huggingface.provider import Dummy

# Step 1: Create Feedback Functions
provider = Dummy(
    loading_prob=0.0,
    freeze_prob=0.0,
    error_prob=0.0,
    overloaded_prob=0.0,
    rpm=1000,
    alloc=1024,
)

f_dummy1 = Feedback(provider.language_match, name="language match").on_input_output()
f_dummy2 = Feedback(provider.positive_sentiment, name="output sentiment").on_output()
f_dummy3 = Feedback(provider.positive_sentiment, name="input sentiment").on_input()

feedbacks = [f_dummy1, f_dummy2, f_dummy3]
expected_feedback_names = {f.name for f in feedbacks}

# Step 2: Initialize TruSession and TruChain
session = TruSession()
prompt = PromptTemplate.from_template("Honestly answer this question: {question}.")
llm = OpenAI(temperature=0.0, streaming=False, cache=False)
chain = prompt | llm | StrOutputParser()
tru_app = TruChain(app=chain, app_id='Chain1_ChatApplication', feedbacks=feedbacks)

# Step 3: Run Feedback Functions
with tru_app as recording:
    chain.run("hello")

record = recording.get()

feedback_results = list(
    session.run_feedback_functions(
        record=record,
        feedback_functions=feedbacks,
        app=tru_app,
        wait=True,
    )
)

# Add to db
session.add_feedbacks(feedback_results)

# Step 4: Verify Results
_, returned_feedback_names = session.get_records_and_feedback(
    app_ids=[tru_app.app_id]
)

assert expected_feedback_names == set(returned_feedback_names), "Feedback result names do not match requested feedback names"
```

This code ensures that the feedback function columns are created and populated correctly on the first run. The `run_feedback_functions` method is used to execute the feedback functions, and `add_feedbacks` adds the results to the database. Finally, `get_records_and_feedback` retrieves and verifies the feedback results <sup>[[1]](https://github.com/truera/trulens/blob/main/tests/e2e/test_tru_session.py)</sup><sup>[[2]](https://github.com/truera/trulens/blob/main/src/core/trulens/core/feedback/feedback.py)</sup><sup>[[3]](https://github.com/truera/trulens/blob/main/docs/trulens_eval/evaluation/running_feedback_functions/with_app.md)</sup>.


<!-- Dosu Comment Footer -->
*To continue talking to [Dosu](https://dosu.dev), mention @dosu.*


@dosu @sfc-gh-srudenko this worked for the first few calls but then afterwards it has stopped populating my results. It returns NaN for language_match and toxic. And has a 1 from a previous call I made to my LLM. Below is a code snippet. 

```
f_pii_detection = Feedback(hugs.pii_detection_with_cot_reasons).on_input()
f_hallucination = Feedback(hugs.language_match).on_input_output()
f_toxicity = Feedback(hugs.toxic).on_input()


tru_recorder = TruChain(
    llm_chain, app_name="Chain1_ChatApplication", feedbacks=[f_pii_detection, f_hallucination, f_toxicity]
)

with tru_recorder as recording:
    prompt_input = "Who is Bob the dog?"
    llm_response = llm_chain(prompt_input)


record = recording.get()

feedback_results = list(
    session.run_feedback_functions(
        record=record,
        feedback_functions=[f_pii_detection, f_hallucination, f_toxicity],
        app=tru_recorder,
        wait=True,
    )
)

# Add to db
session.add_feedbacks(feedback_results)    
    

dataframe, feedback = session.get_records_and_feedback()
    
# Print the DataFrame
for i in range(len(dataframe)):
    print(dataframe.iloc[i])
```

When I take a closer look, the toxic_calls and language_match_calls retun [] where as in previous runs, they were populated with the input and output text. 


@dosu this worked for the first few calls but then afterwards it has stopped populating my results. It returns NaN for language_match and toxic. And has a 1 from a previous call I made to my LLM. Below is a code snippet.

```
f_pii_detection = Feedback(hugs.pii_detection_with_cot_reasons).on_input()
f_hallucination = Feedback(hugs.language_match).on_input_output()
f_toxicity = Feedback(hugs.toxic).on_input()


tru_recorder = TruChain(
    llm_chain, app_name="Chain1_ChatApplication", feedbacks=[f_pii_detection, f_hallucination, f_toxicity]
)

with tru_recorder as recording:
    prompt_input = "Who is Bob the dog?"
    llm_response = llm_chain(prompt_input)


record = recording.get()

feedback_results = list(
    session.run_feedback_functions(
        record=record,
        feedback_functions=[f_pii_detection, f_hallucination, f_toxicity],
        app=tru_recorder,
        wait=True,
    )
)

# Add to db
session.add_feedbacks(feedback_results)    
    

dataframe, feedback = session.get_records_and_feedback()
    
# Print the DataFrame
for i in range(len(dataframe)):
    print(dataframe.iloc[i])
```

When I take a closer look, the toxic_calls and language_match_calls retun [] where as in previous runs, they were populated with the input and output text.

Reposting to correctly tag @dosu 

Hey @paul-gleeson - @dosu's suggestion is not accurate.

There is an expected delay between when you log the app's execution and the feedback computation being completed. We handle the feedback function computation async/in the background by default, so you may not see it if you look at the dashboard before it's ready.

If you want to wait until the feedback is completed, you can use the below:

```
with tru_recorder as recording:
    prompt_input = "What is the typical weather in Ireland?"
    llm_response = llm_chain(prompt_input)
    
record = recording.get()

# Wait for the result futures to be completed and print them.
for feedback, result in record.wait_for_feedback_results().items():
    print(feedback.name, result.result)
```


Ok thanks for the help!