Skip to content

feat(chat-agent): Add a cooking chat example in python#4

Open
nina-kollman wants to merge 1 commit into
mainfrom
nk/chat_example
Open

feat(chat-agent): Add a cooking chat example in python#4
nina-kollman wants to merge 1 commit into
mainfrom
nk/chat_example

Conversation

@nina-kollman

@nina-kollman nina-kollman commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added an interactive Cooking Assistant chat experience with conversation memory.
    • Supports natural back-and-forth dialogue and simple commands to exit.
    • Added per-message conversation tracing for easier session visibility.
    • Added a convenient cooking-chat command to launch the assistant.
  • Documentation

    • Updated the Python examples overview and added setup, usage instructions, and sample chat transcripts for the Cooking Assistant.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a synchronous Cooking Assistant chat REPL with per-message Traceloop tracing, OpenAI conversation history, graceful exit handling, a cooking-chat console command, and README documentation with usage examples.

Changes

Cooking Assistant Chat

Layer / File(s) Summary
Chat workflow and tracing
python/cooking_chat.py
Initializes Traceloop and OpenAI settings, processes each user turn through the traced handle_turn workflow, maintains conversation history, and associates traces with conversation and turn identifiers.
CLI entry point and documentation
python/pyproject.toml, python/README.md
Adds the cooking-chat console entry point and documents the third Python example, its REPL behavior, tracing, and usage transcript.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant main
  participant Traceloop
  participant handle_turn
  participant OpenAI
  User->>main: Enter chat message
  main->>Traceloop: Set conversation_id and turn
  main->>handle_turn: Submit history and message
  handle_turn->>OpenAI: Request chat completion
  OpenAI-->>handle_turn: Return assistant reply
  handle_turn-->>main: Return reply
  main-->>User: Display response
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding a cooking chat example in the Python package.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch nk/chat_example

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
python/cooking_chat.py (1)

29-33: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Make prompt-content tracing an explicit privacy choice.

Traceloop records prompts and completions in span attributes by default. This REPL accepts free-form dietary or health-related input, so document that behavior and expose the documented opt-out, or disable content capture by default. (docs.traceloop.com)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cooking_chat.py` around lines 29 - 33, Update the Traceloop.init
configuration to make prompt and completion content capture an explicit privacy
choice: document the default recording behavior and expose the documented
opt-out, or configure content capture disabled by default. Keep the existing
app_name and batching settings unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/cooking_chat.py`:
- Around line 58-68: Update handle_turn() to build the API request from a
temporary message list without mutating history, then append both user and
assistant messages only after a successful response. In main(), catch expected
OpenAI timeout, rate-limit, and network exceptions around each turn, report the
failure, and continue the REPL loop so retries do not duplicate messages or
terminate the session.
- Around line 51-62: Update handle_turn to bound the conversation history before
constructing the messages argument, retaining only a recent window of turns or
using the existing summarization mechanism if available. Preserve the system
prompt and in-place accumulation behavior while ensuring older history cannot
cause unbounded request growth.
- Around line 96-98: Update the Traceloop.set_association_properties call to
convert turn to a string with str(turn), while leaving conversation_id
unchanged.
- Around line 65-67: Update the response handling around
response.choices[0].message in the cooking chat flow to handle None content
before appending to history. Use the message refusal as the fallback when
available, or stop with a clear error if neither text content nor refusal
exists; only append a non-null assistant message to history.

In `@python/README.md`:
- Around line 120-126: Add a `text` or `console` language tag to the opening
fence of the chickpeas-and-spinach conversation example in the README, leaving
the example content unchanged.

---

Nitpick comments:
In `@python/cooking_chat.py`:
- Around line 29-33: Update the Traceloop.init configuration to make prompt and
completion content capture an explicit privacy choice: document the default
recording behavior and expose the documented opt-out, or configure content
capture disabled by default. Keep the existing app_name and batching settings
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e2b48f75-eb23-421d-b084-3918d3386486

📥 Commits

Reviewing files that changed from the base of the PR and between ccde70e and 77b325f.

📒 Files selected for processing (3)
  • python/README.md
  • python/cooking_chat.py
  • python/pyproject.toml

Comment thread python/cooking_chat.py
Comment on lines +51 to +62
def handle_turn(history: list[dict], user_message: str) -> str:
"""Handle a single user->bot exchange.

Called at the top level (no enclosing span), so each invocation is a root
span == one trace per message exchange. `history` is mutated in place so the
conversation accumulates memory across turns.
"""
history.append({"role": "user", "content": user_message})

response = client.chat.completions.create(
model=MODEL,
messages=[{"role": "system", "content": SYSTEM_PROMPT}, *history],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the file and inspect relevant sections with line numbers.
git ls-files python/cooking_chat.py
printf '\n--- file excerpt ---\n'
cat -n python/cooking_chat.py | sed -n '1,220p'

printf '\n--- history-related search ---\n'
rg -n "history|summary|token|truncate|budget|max_" python/cooking_chat.py

Repository: traceloop/agent-examples

Length of output: 4766


Bound the conversation history in handle_turn. history is sent back in full on every turn, so a long session will keep increasing request size, latency, and cost and can eventually hit the model context limit. Trim to a recent window or summarize older turns before building messages.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cooking_chat.py` around lines 51 - 62, Update handle_turn to bound the
conversation history before constructing the messages argument, retaining only a
recent window of turns or using the existing summarization mechanism if
available. Preserve the system prompt and in-place accumulation behavior while
ensuring older history cannot cause unbounded request growth.

Comment thread python/cooking_chat.py
Comment on lines +58 to +68
history.append({"role": "user", "content": user_message})

response = client.chat.completions.create(
model=MODEL,
messages=[{"role": "system", "content": SYSTEM_PROMPT}, *history],
temperature=0.7,
)
reply = response.choices[0].message.content

history.append({"role": "assistant", "content": reply})
return reply

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

sed -n '1,180p' python/cooking_chat.py

Repository: traceloop/agent-examples

Length of output: 3438


Keep API failures inside the REPL

  • handle_turn() appends the user message before the API call, so a retry can duplicate it.
  • Build the request from a temporary message list, append to history only after success, and catch expected OpenAI timeout/rate-limit/network errors in main() so a failed turn doesn’t exit the loop.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cooking_chat.py` around lines 58 - 68, Update handle_turn() to build
the API request from a temporary message list without mutating history, then
append both user and assistant messages only after a successful response. In
main(), catch expected OpenAI timeout, rate-limit, and network exceptions around
each turn, report the failure, and continue the REPL loop so retries do not
duplicate messages or terminate the session.

Comment thread python/cooking_chat.py
Comment on lines +65 to +67
reply = response.choices[0].message.content

history.append({"role": "assistant", "content": reply})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
cd python
uv run python - <<'PY'
from openai.types.chat import ChatCompletionMessage
print(ChatCompletionMessage.model_fields["content"])
print(ChatCompletionMessage.model_fields.get("refusal"))
PY

Repository: traceloop/agent-examples

Length of output: 202


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,180p' python/cooking_chat.py

Repository: traceloop/agent-examples

Length of output: 3438


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== dependency pins ==\n'
git ls-files | rg '(^|/)(pyproject\.toml|requirements(\.txt)?|poetry\.lock|uv\.lock|Pipfile|Pipfile\.lock)$' || true

printf '\n== openai references ==\n'
rg -n "openai|ChatCompletionMessage|refusal|tool_call|tool-call" python . -g '!**/.git/**' || true

Repository: traceloop/agent-examples

Length of output: 15767


🌐 Web query:

OpenAI Chat Completions assistant message content refusal optional schema content=None refusal field next request invalid

💡 Result:

In the OpenAI Chat Completions API, the refusal field is an optional property of an assistant message, distinct from the content field [1][2][3]. When a model generates a refusal (e.g., due to safety policies or when using Structured Outputs), the API may return a message where content is null and refusal contains the string explanation of the refusal [2][3][4]. Key technical points regarding this behavior include: 1. Relationship between Content and Refusal: The content field is not always required to be present if a refusal is provided, particularly in cases where the model has explicitly refused to answer [2][3]. In many programmatic implementations, content is defined as Optional[str] and refusal as Optional[str] [5][2][6]. 2. API Design: The refusal field is intended to be surfaced to the user or handled by application logic when it appears [4]. Applications consuming the API should check for the presence of the refusal field rather than assuming content will contain the model's response [4][7]. 3. Handling in SDKs: Various SDKs (such as Python, Node, and Java) reflect this structure, providing both fields as optional [2][3][6]. Issues reported in developer communities often stem from downstream applications or wrappers that do not explicitly check for or gracefully handle the refusal field when content is null, sometimes causing errors or infinite retry loops [8][9][7]. If you receive a response with content: None and refusal: "...", it indicates a model-generated refusal. Your application logic should be updated to detect the refusal property, handle it as a legitimate assistant response (e.g., by displaying the refusal message), and stop further processing for that turn [4][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '684,704p' python/travel_agent.py

Repository: traceloop/agent-examples

Length of output: 939


Handle assistant turns without text content
response.choices[0].message.content can be None on refusal/tool-call responses. Don’t append that directly to history; fall back to message.refusal or stop with a clear error before storing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cooking_chat.py` around lines 65 - 67, Update the response handling
around response.choices[0].message in the cooking chat flow to handle None
content before appending to history. Use the message refusal as the fallback
when available, or stop with a clear error if neither text content nor refusal
exists; only append a non-null assistant message to history.

Comment thread python/cooking_chat.py
Comment on lines +96 to +98
Traceloop.set_association_properties(
{"conversation_id": conversation_id, "turn": turn}
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
cd python
uv run python - <<'PY'
import inspect
from traceloop.sdk import Traceloop

print(inspect.signature(Traceloop.set_association_properties))
print(inspect.getsource(Traceloop.set_association_properties))
PY

Repository: traceloop/agent-examples

Length of output: 202


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file and nearby context
wc -l python/cooking_chat.py
cat -n python/cooking_chat.py | sed -n '1,180p'

# Find Traceloop usage and dependency pinning
rg -n "set_association_properties|traceloop" python -S

# Inspect dependency files for the SDK version
fd -a "pyproject.toml|requirements*.txt|poetry.lock|uv.lock|Pipfile.lock" python

Repository: traceloop/agent-examples

Length of output: 6660


🌐 Web query:

Traceloop set_association_properties association properties string values turn integer docs

💡 Result:

According to the official Traceloop documentation, the association properties used to attach unique identifiers to traces must be strings [1][2]. Specifically, the documentation explicitly states that "the only requirement is that the key and the value are strings" [1][2]. If you pass non-string values (such as integers) to set_association_properties, they may not be handled as expected by the backend or could cause errors, as the system design typically enforces string-based key-value pairs for these associations [1][2]. To ensure your properties are correctly captured and searchable in the Traceloop UI, you should explicitly cast or convert any integer values to strings before passing them to the set_association_properties method. Example of correct usage: Traceloop.set_association_properties({ "user_id": str(12345), "chat_id": "chat_abc" })

Citations:


Pass turn as a string. Traceloop association properties require string values, so turn should be str(turn); otherwise the session-grouping metadata can be dropped or rejected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cooking_chat.py` around lines 96 - 98, Update the
Traceloop.set_association_properties call to convert turn to a string with
str(turn), while leaving conversation_id unchanged.

Comment thread python/README.md
Comment on lines +120 to +126
```
you > what can I make with chickpeas and spinach?
chef > ...
you > make it vegetarian and quicker
chef > ...
you > exit
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language tag to the fenced example.

markdownlint-cli2 reports MD040 for this block. Use text or console after the opening fence.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 120-120: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/README.md` around lines 120 - 126, Add a `text` or `console` language
tag to the opening fence of the chickpeas-and-spinach conversation example in
the README, leaving the example content unchanged.

Source: Linters/SAST tools

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.

1 participant