fix(llma): extract pydantic-ai tool calls from output parts#59755
Merged
carlos-marchal-ph merged 3 commits intoMay 27, 2026
Merged
Conversation
Teach tool-call extraction to read Pydantic AI OTel output message parts. Pydantic AI emits assistant tool calls as parts with type="tool_call" and a top-level name; normalize those into the existing $ai_tools_called and $ai_tool_call_count fields. Add extractor coverage for single, multiple, mixed, wrapped, malformed, and stringified JSON shapes, plus a processAiEvent OTel ingestion test.
Contributor
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
nodejs/src/ingestion/ai/tools/extract-tool-calls.test.ts:681-709
This is the only new test in `processAiToolCallExtraction` that isn't parameterised. Given that the team always prefers parameterised tests, this test (along with the existing non-parameterised neighbours) could be rolled into an `it.each` table in the same style used for `extractToolCallNames` above, making it easy to add further provider shapes later.
```suggestion
it.each([
[
'Pydantic AI OTel tool_call parts from stringified JSON',
JSON.stringify([
{
role: 'assistant',
parts: [
{ type: 'text', content: 'Let me check.' },
{ type: 'tool_call', id: 'call_abc', name: 'get_weather', arguments: '{"city":"NYC"}' },
{ type: 'tool_call', id: 'call_def', name: 'search_docs', arguments: '{"q":"weather"}' },
],
},
]),
'get_weather,search_docs',
2,
],
])('%s', (_description, outputChoices, expectedToolsCalled, expectedCount) => {
const event = createEvent('$ai_generation', { $ai_output_choices: outputChoices })
const result = processAiToolCallExtraction(event)
expect(result.properties!['$ai_tools_called']).toBe(expectedToolsCalled)
expect(result.properties!['$ai_tool_call_count']).toBe(expectedCount)
})
```
Reviews (1): Last reviewed commit: "fix(llma): extract pydantic-ai tool call..." | Re-trigger Greptile |
Address review feedback by converting the Pydantic AI processAiToolCallExtraction case to an it.each table.
Contributor
carlos-marchal-ph
left a comment
There was a problem hiding this comment.
Looks good to me, thanks for the contribution! I'm gonna go ahead with the test change I suggested and then merge it.
Comment on lines
+681
to
+706
| it.each([ | ||
| [ | ||
| 'Pydantic AI OTel tool_call parts from stringified JSON', | ||
| JSON.stringify([ | ||
| { | ||
| role: 'assistant', | ||
| parts: [ | ||
| { type: 'text', content: 'Let me check.' }, | ||
| { | ||
| type: 'tool_call', | ||
| id: 'call_abc', | ||
| name: 'get_weather', | ||
| arguments: '{"city":"NYC"}', | ||
| }, | ||
| { | ||
| type: 'tool_call', | ||
| id: 'call_def', | ||
| name: 'search_docs', | ||
| arguments: '{"q":"weather"}', | ||
| }, | ||
| ], | ||
| }, | ||
| ]), | ||
| 'get_weather,search_docs', | ||
| 2, | ||
| ], |
Contributor
There was a problem hiding this comment.
No need to make this a tabular test since it has a single input
carlos-marchal-ph
approved these changes
May 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Pydantic AI's current OpenTelemetry instrumentation stores generation output in
gen_ai.output.messages, which PostHog maps to$ai_output_choices. Tool calls in that output are represented as messagepartswithtype: "tool_call"and a top-levelname.PostHog already extracts tool calls from several provider shapes, but not this Pydantic AI message-parts shape, so affected
$ai_generationevents do not get$ai_tools_calledor$ai_tool_call_countpopulated.Pydantic AI instrumentation docs: https://pydantic.dev/docs/ai/api/models/instrumented/
Changes
parts[]arrays on output choices and message wrappers.part.namewhenpart.type === "tool_call".How did you test this code?
I tested this locally in Docker.
All passed.
pnpm exec jest --runInBand --forceExit \ src/ingestion/ai/tools/extract-tool-calls.test.ts \ src/ingestion/ai/otel/attribute-mapping.test.ts \ src/ingestion/ai/process-ai-event.test.tsResult: 3 suites passed, 288 tests passed.
Passed.
Publish to changelog?
Do not publish to changelog.
Docs update
No docs update. This is a backend ingestion compatibility fix for an already documented tools normalization field.
🤖 Agent context
I used Codex to help me with this PR.
Codex helped inspect the existing tool-call extractor patterns, implement the narrow Pydantic AI
parts[].type === "tool_call"extraction path, and add focused tests.The implementation stays local to the existing extractor and reuses the current sanitizer, ordering, duplicate preservation, and count behavior. The added tests cover wrapped and unwrapped output shapes, malformed parts, mixed text/tool parts, stringified JSON, and the full OTel ingestion path.