Tool argument errors put the argument values in the log message body, outside `lk.pii.*`
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- Medio día
- Aptitud para principiantes
- 25/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- python
- Área
- observability
Línea de trabajo
The message comes from prepare_function_arguments in livekit/agents/llm/utils.py, and the warning is logged in perform_tool_executions in voice/generation.py. Read telemetry/traces.py to see how lk.pii. fields are handled. Done means the ToolError warning logs a static message with the error text under a lk.pii. key. Pull request #7804 is already open against this issue, so check it before starting.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Bug Description
When a tool call's arguments can't be prepared, prepare_function_arguments raises a ToolError whose message includes the arguments:
- a validation error (valid JSON, wrong type) embeds the pydantic error text, which includes
input_value=...(code) - JSON that
json_repaircan't recover embeds the first 200 characters of the raw arguments (code, re-raised at L687)
perform_tool_executions then logs that message in the body of ToolError while executing tool: %s (code).
The same failure is also logged as error parsing arguments for ... with the arguments in lk.pii.arguments, so the value is marked there. The ToolError warning has no lk.pii. field, so the value is in the body, where it can't be dropped ("a log body cannot be redacted", traces.py). This is the same class as #7156 (participant identity in log bodies), at a call site that PR doesn't touch.
Expected Behavior
The argument values are not in the log body. For example, the warning logs a static message and puts the error text under a lk.pii. key, as error parsing arguments for ... already does. The ToolError returned to the model can keep the detail.
Reproduction Steps
Only livekit-agents, no audio or network. A scripted LLM returns one tool call, then a text reply, and the logs go through the CLI's JsonFormatter.
SECRET = "[email protected]"
@function_tool
async def lookup_orders(count: int) -> str:
"""Look up the customer's latest orders."""
return "ok"
# scripted LLM: first chat() returns
# FunctionToolCall(name="lookup_orders", arguments=json.dumps({"count": SECRET}), call_id="call_1")
# then a plain text reply
session = AgentSession(llm=ScriptedLLM())
await session.start(agent=Agent(instructions="test", tools=[lookup_orders]))
await session.run(user_input="show my orders")
Output (full script below):
== valid JSON, wrong type: '{"count": "[email protected]"}'
[ERROR] 'error parsing arguments for `lookup_orders`'
value in lk.pii.* fields: ['lk.pii.arguments'] | value in message body: False
[WARNING] 'ToolError while executing tool: Error parsing arguments for `lookup_or'
value in lk.pii.* fields: no | value in message body: True
== truncated JSON: '{"count": "[email protected]'
[WARNING] 'repaired malformed function-call JSON arguments'
value in lk.pii.* fields: ['lk.pii.raw_arguments', 'lk.pii.repaired'] | value in message body: False
[ERROR] 'error parsing arguments for `lookup_orders`'
value in lk.pii.* fields: ['lk.pii.arguments'] | value in message body: False
[WARNING] 'ToolError while executing tool: Error parsing arguments for `lookup_or'
value in lk.pii.* fields: no | value in message body: True
In the second case the SDK repairs the JSON, then validation fails the same way.
Full script
"""Minimal repro: a tool argument error puts the argument value in the log message body.
The SDK marks content-bearing log fields with an `lk.pii.` prefix so that redaction can drop them,
because a log body cannot be redacted. When a tool call's arguments fail validation (a string for an
`int` parameter), the pydantic error text, including `input_value=...`, goes into the ToolError
message. When the arguments are not valid JSON, the first 200 characters of the raw arguments go into
it. `voice/generation.py` then logs that message in the body of `ToolError while executing tool: %s`,
with no `lk.pii.` field, so dropping `lk.pii.*` does not remove the value.
Only livekit-agents is used (no audio, no network). A scripted LLM returns one tool call, then a plain
text reply. Logs are written with the CLI's JsonFormatter.
pip install "livekit-agents==1.8.6"
python repro.py
"""
import asyncio
import io
import json
import logging
from livekit.agents import Agent, AgentSession, function_tool, llm
from livekit.agents.cli.log import JsonFormatter
from livekit.agents.types import DEFAULT_API_CONNECT_OPTIONS
SECRET = "[email protected]" # stands in for user data the model put in the arguments
CASES = {
"valid JSON, wrong type": json.dumps({"count": SECRET}),
"truncated JSON": '{"count": "' + SECRET,
}
class ScriptedLLM(llm.LLM):
def __init__(self, arguments: str) -> None:
super().__init__()
self.calls = 0
self.arguments = arguments
def chat(self, *, chat_ctx, tools=None, conn_options=DEFAULT_API_CONNECT_OPTIONS, **kwargs):
self.calls += 1
return ScriptedStream(
self, chat_ctx=chat_ctx, tools=tools or [], conn_options=conn_options, turn=self.calls
)
class ScriptedStream(llm.LLMStream):
def __init__(self, llm_, *, turn, **kwargs) -> None:
super().__init__(llm_, **kwargs)
self._turn = turn
async def _run(self) -> None:
if self._turn == 1:
call = llm.FunctionToolCall(name="lookup_orders", arguments=self._llm.arguments, call_id="call_1")
delta = llm.ChoiceDelta(role="assistant", tool_calls=[call])
else:
delta = llm.ChoiceDelta(role="assistant", content="Sorry, could you repeat that?")
self._event_ch.send_nowait(llm.ChatChunk(id=f"chunk_{self._turn}", delta=delta))
@function_tool
async def lookup_orders(count: int) -> str:
"""Look up the customer's latest orders.
Args:
count: how many orders to return
"""
return "ok"
async def run_case(name: str, arguments: str) -> None:
print(f"== {name}: {arguments!r}")
out = io.StringIO()
handler = logging.StreamHandler(out)
handler.setFormatter(JsonFormatter())
root = logging.getLogger()
root.handlers = [handler]
root.setLevel(logging.INFO)
session = AgentSession(llm=ScriptedLLM(arguments))
await session.start(agent=Agent(instructions="test", tools=[lookup_orders]))
await session.run(user_input="show my orders")
await session.aclose()
lines = [json.loads(line) for line in out.getvalue().splitlines()]
for line in lines:
if SECRET not in json.dumps(line):
continue
marked = [k for k, v in line.items() if k.startswith("lk.pii.") and SECRET in json.dumps(v)]
in_body = SECRET in line.get("message", "")
print(f" [{line['level']}] {line['message'][:70]!r}")
print(f" value in lk.pii.* fields: {marked or 'no'} | value in message body: {in_body}")
leaked = [line for line in lines if SECRET in line.get("message", "")]
print(f" records with the value in the message body: {len(leaked)}")
async def main() -> None:
for name, arguments in CASES.items():
await run_case(name, arguments)
asyncio.run(main())
Operating System
Windows 11
Models Used
None (scripted LLM). In a real call this needs the model to send arguments that fail validation, for example a customer's email or phone number in a field of another type.
Package Versions
livekit-agents==1.8.6
python==3.13
The same code is on main and in [email protected].
Additional Context
We drop lk.pii.* from our stdout logs before they reach CloudWatch. In the tool path, this warning is where we found the value still getting through.
- Lenguaje dominante
- Python
- Estrellas
- 14.6k
- Forks
- 3.9k
- Merge medio
- 3 d 3 h
- PR fusionados (30 d)
- 171
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de livekit/agents
-
Assistant markup is stripped from chat history when expressive mode is offPosiblemente ocupada @ShivamSharma43 la tomó hace 7 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
docs: openai.LLM vs openai.responses.LLM for HTTP OpenAI-compat hostsQuizá libre de nuevo @AbarnaaSree la tomó hace 46 días y no hay ningún pull request abierto. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
livekit/agents#7022 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
google: support Live Avatar video output for Gemini 3.8 Live (avatar_config, VIDEO modality)Abierto
Dificultad 5/5 Más de una semana Aptitud para principiantes 22/100
livekit/agents#7843 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 40/100
Los mantenedores suelen responder en 1 día
-
StreamPacerWrapper.end_input() sets _input_ended before buffered sentences reach _sentences; last sentence(s) silently never sent to TTSPosiblemente ocupada @Hugo-DDT la tomó hoy. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 25/100
Los mantenedores suelen responder en 1 día
Todos los issues de livekit/agents
Issues similares
-
md/asciidoc/csv: an explicit `encoding="utf-8"` keeps a leading BOM, the auto-detect path drops itAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
docling-project/docling#4846 ·
Los mantenedores suelen responder en 1 día
-
area/sessions comp/dashboard comp/desktop comp/gateway comp/tui duplicate P2 sweeper:risk-session-state type/bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
NousResearch/hermes-agent#138914 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
bot-triaged module: activation checkpointing module: inductor oncall: pt2 triaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
Los mantenedores suelen responder en 1 día
-
[oblt-aw][autodoc] Update PR Actions Detective E2E prerequisites and fix dashboard-format table typoAbiertodocumentation
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
elastic/oblt-aw#2382 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
Los mantenedores suelen responder en 1 día