# LangSmithSecret

> **Class** in `langsmith`

📖 [View in docs](https://reference.langchain.com/python/langsmith/secret/LangSmithSecret)

A string that LangSmith serializes as ``[LANGSMITH SECRET]``.

Wrap a credential once, where it is read, and LangSmith masks it wherever
it appears in a trace, at any nesting depth::

    @traceable
    def call_vendor(api_key: str, prompt: str) -> str: ...

    call_vendor(api_key=LangSmithSecret(key), prompt="hi")
    # traced inputs: {"api_key": "[LANGSMITH SECRET]", "prompt": "hi"}

It is a real ``str`` everywhere else: ``json.dumps``, logging and
third-party clients all see the true value. Operations that derive a new
string return a ``LangSmithSecret`` again, so the marker is not lost by
``.strip()`` or slicing.

Known limits:

- ``f"Bearer {secret}"``, ``"".join([secret])`` and
  ``"Bearer {}".format(secret)`` yield a plain ``str``: those build the
  result in C, where the operand gets no say in its type. Wrap the finished
  value instead -- ``LangSmithSecret(f"Bearer {key}")``. Concatenation and
  ``%`` are covered, in either operand order.
- ``secret.encode()`` returns the real bytes, by design.
- A secret used as a **dict key** is masked, but two of them in one dict
  collapse to a single entry. A key held inside a dataclass is not masked:
  orjson serializes those itself.
- An object whose own ``__str__`` interpolates a secret leaks it wherever
  LangSmith stringifies that object. ``__repr__`` is masked; ``__str__``
  belongs to the object.
- Nothing inside a **pydantic model** is masked. Pydantic serializes its
  own fields and LangSmith deliberately does not override that: a hook
  there would also replace the credential with the mask when a vendor
  client dumps the model to build a request.

## Signature

```python
LangSmithSecret()
```

## Extends

- `str`

---

[View source on GitHub](https://github.com/langchain-ai/langsmith-sdk/blob/19d52419f47c93ca2c57a0d51c58d255f5949092/python/langsmith/secret.py#L60)