Updating observations, and ending them by hand
update_current_span adds details to the running observation while it runs. start_observation opens one you end yourself, and an observation never ended is never sent.
So far every observation opened and closed with a function or a with block, and its input and output were known up front. Two things do not fit: details learned halfway through a step, and a step that ends later, somewhere else in the code.
Adding details to the running observation
@observe(name="lookup-order", as_type="tool")
def lookup_order(order_id):
status = ORDERS.get(order_id, "not found")
langfuse.update_current_span(metadata={"warehouse": "Leeds", "found": status != "not found"})
return statusupdate_current_span changes whichever observation is running, without needing a variable that holds it. Metadata is a dictionary of extra facts you can filter by later; it belongs here rather than in the name.
lookup_order("A17")
langfuse.flush()
attributes = local_langfuse.SPANS[0]["attributes"]
print({key: value for key, value in attributes.items() if "metadata" in key})python update.pyEach metadata key is exported as its own attribute, named langfuse.observation.metadata. followed by the key, which is how Langfuse can filter on one key at a time.
An observation you end yourself
A refund waits for a person to approve it. That can take hours, and the approval arrives in different code from the request. start_observation opens an observation without making it the running one, and returns it so you can finish it later.
approval = langfuse.start_observation(name="refund-approval", input="Refund B22, 40 euros")
print("waiting for a person to approve")
approval.update(output="approved by Sam")
langfuse.flush()
local_langfuse.tree("input", "output")python approval.pyThe tree is empty, and there was no error. An observation is only exported when it ends, and nothing ended this one. The Langfuse documentation warns about exactly this: with start_observation, calling end is your job.
approval = langfuse.start_observation(name="refund-approval", input="Refund B22, 40 euros")
print("waiting for a person to approve")
approval.update(output="approved by Sam")
approval.end()
langfuse.flush()
local_langfuse.tree("input", "output")python approval.pyWith approval.end() the observation is complete and exported, with its input and output. The with form and @observe call end for you; use start_observation only when the start and the end happen in different places.
- Add
"order_id": order_idto the metadata and runupdate.pyagain. - Create a child with
approval.start_observation(name="notify-customer"), end both, and print the tree. - Call
langfuse.update_current_spanoutside any observation and read the log message.
Every expert started right here.