LangfuseLangfuse Python SDK 4.15.4 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
38 small wins to finish your pathNext lesson

@observe: traces from function calls

@observe records a function call as an observation, with its arguments as input and its return value as output. Calls inside it nest under it.

Lesson 2 wrapped the desk in a with block by hand. That records one step. The desk has two: reading the ticket and looking up the order. @observe records each function you decorate, and builds the tree from which function called which.

Exampleobserved.py
from langfuse import Langfuse, observe

import local_langfuse

url = local_langfuse.start()
langfuse = Langfuse(public_key="pk-lf-local", secret_key="sk-lf-local", base_url=url)

The setup lines from lesson 2, with observe added to the first import.

Exampleobserved.py, continued
import re

ORDERS = {"A17": "shipped on 3 March"}


@observe()
def lookup_order(order_id):
    return ORDERS.get(order_id, "not found")

The lookup is now a function of its own, so it can be recorded separately.

Exampleobserved.py, continued
@observe()
def answer(ticket):
    order = re.search(r"[A-Z]\d{2}", ticket)
    if not order:
        return "Could you send your order number?"
    return f"Order {order.group()}: {lookup_order(order.group())}"

answer calls it. The rest of answer is as it was.

Exampleobserved.py, continued
print(answer("Where is my order A17?"))
print(answer("Hello?"))

langfuse.flush()
local_langfuse.tree("input", "output")
Example
python observed.py

Two calls to answer, two traces. In the first, lookup_order ran inside answer, so its observation is a child of answer's. The second ticket had no order id, so the lookup never ran. Each observation is named after its function.

The input is the call's arguments, serialised as JSON with args and kwargs. The output is the return value. Whatever you pass to a decorated function is therefore sent to Langfuse, which part 4 comes back to.

Decorator and context manager together

Both ways of recording use the same OpenTelemetry context, which tracks the observation that is running now. A decorated function called inside a with langfuse.start_as_current_observation(...) block becomes its child, and the other way round. Later lessons use whichever reads better for the code at hand.

Try it yourself
  • Wrap the two answer calls in one with langfuse.start_as_current_observation(name="inbox") block and print the tree.
  • Call lookup_order(order_id="A17") with a keyword and read its input.
  • Pass name="lookup-order" to the @observe on lookup_order.

Slow is fine. Stopping is the only problem.