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

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

Exampleupdate.py, after the setup lines and ORDERS from lesson 3
@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 status

update_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.

Exampleupdate.py, continued
lookup_order("A17")

langfuse.flush()
attributes = local_langfuse.SPANS[0]["attributes"]
print({key: value for key, value in attributes.items() if "metadata" in key})
Example
python update.py

Each 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.

Exampleapproval.py, after the setup lines
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")
Example
python approval.py

The 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.

Exampleapproval.py, fixed
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")
Example
python approval.py

With 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.

Try it yourself
  • Add "order_id": order_id to the metadata and run update.py again.
  • Create a child with approval.start_observation(name="notify-customer"), end both, and print the tree.
  • Call langfuse.update_current_span outside any observation and read the log message.

Every expert started right here.