@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.
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.
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.
@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.
print(answer("Where is my order A17?"))
print(answer("Hello?"))
langfuse.flush()
local_langfuse.tree("input", "output")python observed.pyTwo 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.
- Wrap the two
answercalls in onewith 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@observeonlookup_order.
Slow is fine. Stopping is the only problem.