Pydantic does some fun things where they decide they'll deep copy a model when you do .copy() if you have fields that are excluded.
from pydantic import BaseModel, Field
class Foo1(BaseModel):
a: dict
b: str
class Foo2(BaseModel):
a: dict
b: str = Field(exclude=True)
a = Foo1(a={"a": 1, "b": 2}, b="hello")
b = a.copy()
print(id(a.a) == id(b.a)) # True
c = Foo2(a={"a": 1, "b": 2}, b="hello")
d = c.copy()
print(id(c.a) == id(d.a)) # False
This usually isn't a problem but can be annoying if you have something like a generator in your inputs dict (where deep copying then would raise an exception), and it has a perf penalty.
This PR addresses this. It also lets us lazily initilaize the client so we can use a RunTree as a stand-in for a Run object in lagnchain and only ever actually get the API key validated / client created if we do enable tracing or try to access the client
Pydantic does some fun things where they decide they'll deep copy a model when you do
.copy()
if you have fields that are excluded.This usually isn't a problem but can be annoying if you have something like a generator in your inputs dict (where deep copying then would raise an exception), and it has a perf penalty.
This PR addresses this. It also lets us lazily initilaize the client so we can use a RunTree as a stand-in for a
Run
object in lagnchain and only ever actually get the API key validated / client created if we do enable tracing or try to access the client