|
33 | 33 | LANGSMITH_METADATA = sys.intern(f"{LANGSMITH_PREFIX}metadata")
|
34 | 34 | LANGSMITH_TAGS = sys.intern(f"{LANGSMITH_PREFIX}tags")
|
35 | 35 | LANGSMITH_PROJECT = sys.intern(f"{LANGSMITH_PREFIX}project")
|
| 36 | +OVERRIDE_OUTPUTS = sys.intern("__omit_auto_outputs") |
| 37 | +NOT_PROVIDED = cast(None, object()) |
36 | 38 | _CLIENT: Optional[Client] = None
|
37 | 39 | _LOCK = threading.Lock() # Keeping around for a while for backwards compat
|
38 | 40 |
|
@@ -164,6 +166,51 @@ def __setattr__(self, name, value):
|
164 | 166 | else:
|
165 | 167 | return super().__setattr__(name, value)
|
166 | 168 |
|
| 169 | + def set( |
| 170 | + self, |
| 171 | + *, |
| 172 | + inputs: Optional[Mapping[str, Any]] = NOT_PROVIDED, |
| 173 | + outputs: Optional[Mapping[str, Any]] = NOT_PROVIDED, |
| 174 | + tags: Optional[Sequence[str]] = NOT_PROVIDED, |
| 175 | + metadata: Optional[Mapping[str, Any]] = NOT_PROVIDED, |
| 176 | + ) -> None: |
| 177 | + """Set the inputs, outputs, tags, and metadata of the run. |
| 178 | +
|
| 179 | + If performed, this will override the default behavior of the |
| 180 | + end() method to ignore new outputs (that would otherwise be added) |
| 181 | + by the @traceable decorator. |
| 182 | +
|
| 183 | + If your LangChain or LangGraph versions are sufficiently up-to-date, |
| 184 | + this will also override the default behavior LangChainTracer. |
| 185 | +
|
| 186 | + Args: |
| 187 | + inputs: The inputs to set. |
| 188 | + outputs: The outputs to set. |
| 189 | + tags: The tags to set. |
| 190 | + metadata: The metadata to set. |
| 191 | +
|
| 192 | + Returns: |
| 193 | + None |
| 194 | + """ |
| 195 | + if tags is not NOT_PROVIDED: |
| 196 | + self.tags = list(tags) |
| 197 | + if metadata is not NOT_PROVIDED: |
| 198 | + self.extra.setdefault("metadata", {}).update(metadata or {}) |
| 199 | + if inputs is not NOT_PROVIDED: |
| 200 | + # Used by LangChain core to determine whether to |
| 201 | + # re-upload the inputs upon run completion |
| 202 | + self.extra["inputs_is_truthy"] = False |
| 203 | + if inputs is None: |
| 204 | + self.inputs = {} |
| 205 | + else: |
| 206 | + self.inputs = dict(inputs) |
| 207 | + if outputs is not NOT_PROVIDED: |
| 208 | + self.extra[OVERRIDE_OUTPUTS] = True |
| 209 | + if outputs is None: |
| 210 | + self.outputs = {} |
| 211 | + else: |
| 212 | + self.outputs = dict(outputs) |
| 213 | + |
167 | 214 | def add_tags(self, tags: Union[Sequence[str], str]) -> None:
|
168 | 215 | """Add tags to the run."""
|
169 | 216 | if isinstance(tags, str):
|
@@ -204,6 +251,9 @@ def add_inputs(self, inputs: dict[str, Any]) -> None:
|
204 | 251 | if self.inputs is None:
|
205 | 252 | self.inputs = {}
|
206 | 253 | self.inputs.update(inputs)
|
| 254 | + # Set to False so LangChain things it needs to |
| 255 | + # re-upload inputs |
| 256 | + self.extra["inputs_is_truthy"] = False |
207 | 257 |
|
208 | 258 | def add_event(
|
209 | 259 | self,
|
@@ -252,11 +302,14 @@ def end(
|
252 | 302 | ) -> None:
|
253 | 303 | """Set the end time of the run and all child runs."""
|
254 | 304 | self.end_time = end_time or datetime.now(timezone.utc)
|
255 |
| - if outputs is not None: |
256 |
| - if not self.outputs: |
257 |
| - self.outputs = outputs |
258 |
| - else: |
259 |
| - self.outputs.update(outputs) |
| 305 | + # We've already 'set' the outputs, so ignore |
| 306 | + # the ones that are automatically included |
| 307 | + if not self.extra.get(OVERRIDE_OUTPUTS): |
| 308 | + if outputs is not None: |
| 309 | + if not self.outputs: |
| 310 | + self.outputs = outputs |
| 311 | + else: |
| 312 | + self.outputs.update(outputs) |
260 | 313 | if error is not None:
|
261 | 314 | self.error = error
|
262 | 315 | if events is not None:
|
|
0 commit comments