Skip to content

Commit 633d2be

Browse files
committed
fix: classify tool telemetry by execution outcome
Drive tool_call error_type from timed execution outcome instead of scanning result text. Trim homebrew preflight note so it no longer mentions the official installer URL.
1 parent a0914d9 commit 633d2be

2 files changed

Lines changed: 32 additions & 31 deletions

File tree

‎apps/pythinker-code/src/cli/update/preflight.ts‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { log, type Logger } from '@pymodel/pythinker-code-sdk';
44
import type { TelemetryProperties } from '@pymodel/pythinker-telemetry';
55

66
import {
7-
pythinkerCodeOfficialInstallUrl,
87
nativeInstallCommandUnix,
98
nativeInstallCommandWin,
109
} from '#/constant/app';
@@ -186,10 +185,7 @@ function resolveInstallSpawn(
186185

187186
// Built per call: the official-installer URL follows the current region.
188187
function thirdPartySourceNote(): string {
189-
return (
190-
'\nNote: Third-party sources may lag behind the official release.\n' +
191-
`For the latest updates, use the official installer: ${pythinkerCodeOfficialInstallUrl()}\n`
192-
);
188+
return '\nNote: Third-party sources may lag behind the official release.\n';
193189
}
194190

195191
export function renderManualUpdateMessage(

‎packages/agent-core-v2/src/agent/toolExecutor/toolExecutorService.ts‎

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService {
236236
candidates.push(
237237
nextTimed.then(
238238
(result): ToolExecutionStreamEvent => ({ type: 'timed', result }),
239-
(reason): ToolExecutionStreamEvent => ({ type: 'timedRejected', reason }),
239+
(error): ToolExecutionStreamEvent => ({ type: 'timedRejected', error }),
240240
),
241241
);
242242
}
@@ -266,7 +266,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService {
266266
options,
267267
).then(
268268
(value): SettledToolExecutionResult => ({ status: 'fulfilled', value }),
269-
(reason): SettledToolExecutionResult => ({ status: 'rejected', reason }),
269+
(error): SettledToolExecutionResult => ({ status: 'rejected', error }),
270270
);
271271
finalizations.add(finalization);
272272
nextTimed = timedResults.next();
@@ -302,7 +302,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService {
302302
);
303303

304304
this.dispatchToolResult(call, finalized, options);
305-
this.trackToolCall(call, finalized, timedResult.durationMs, options);
305+
this.trackToolCall(call, finalized, timedResult.durationMs, timedResult.outcome, options);
306306

307307
return {
308308
toolCallId: call.toolCall.id,
@@ -315,9 +315,10 @@ export class AgentToolExecutorService implements IAgentToolExecutorService {
315315
call: PreflightedToolCall,
316316
result: ToolResult,
317317
durationMs: number,
318+
executionOutcome: ToolExecutionOutcome,
318319
options: ToolExecutorExecuteOptions,
319320
): void {
320-
const outcome = toolTelemetryOutcome(result);
321+
const outcome = toolTelemetryOutcome(result, executionOutcome);
321322
const toolCallId = call.toolCall.id;
322323
const dupType = this.toolCallDupTypes.get(toolCallId) ?? 'normal';
323324
this.toolCallDupTypes.delete(toolCallId);
@@ -468,13 +469,21 @@ export class AgentToolExecutorService implements IAgentToolExecutorService {
468469
accesses: task.accesses,
469470
start: async () => {
470471
const startedAt = Date.now();
472+
const execution = task.execute(signal);
471473
return {
472-
result: task.execute(signal).then(({ result, outcome }) => ({
474+
result: execution.then(({ result, outcome }) => ({
473475
index,
474476
result,
475477
outcome,
476478
durationMs: Math.max(0, Date.now() - startedAt),
477479
})),
480+
effectsSettled: (async () => {
481+
try {
482+
const run = await execution;
483+
if (run.effectsSettled !== undefined) await run.effectsSettled;
484+
} catch {
485+
}
486+
})(),
478487
};
479488
},
480489
});
@@ -483,7 +492,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService {
483492
index,
484493
pendingResult.then(
485494
(value): SettledTimedToolResult => ({ status: 'fulfilled', value }),
486-
(reason): SettledTimedToolResult => ({ status: 'rejected', index, reason }),
495+
(error): SettledTimedToolResult => ({ status: 'rejected', index, error }),
487496
),
488497
);
489498
}
@@ -534,21 +543,25 @@ export class AgentToolExecutorService implements IAgentToolExecutorService {
534543
},
535544
});
536545
rawResult = await raceWithAbortGrace(executePromise, signal, call.toolName);
546+
const abortedByGrace =
547+
signal.aborted &&
548+
typeof rawResult === 'object' &&
549+
rawResult !== null &&
550+
(rawResult as { isError?: boolean }).isError === true;
551+
return {
552+
result: this.normalizeAndMergeResult(rawResult, call.toolName, execution),
553+
outcome: abortedByGrace ? 'aborted' : 'executed',
554+
};
537555
} catch (error) {
538556
const aborted = isAbortError(error) || signal.aborted;
539557
const output = aborted
540558
? abortedToolOutput(call.toolName, signal.reason)
541559
: `Tool "${call.toolName}" failed: ${errorMessage(error)}`;
542560
return {
543561
result: makeErrorToolResult(call, call.args, output).result,
544-
outcome: 'executed',
562+
outcome: aborted ? 'aborted' : 'executed',
545563
};
546564
}
547-
548-
return {
549-
result: this.normalizeAndMergeResult(rawResult, call.toolName, execution),
550-
outcome: 'executed',
551-
};
552565
}
553566

554567
private normalizeAndMergeResult(
@@ -908,28 +921,20 @@ function normalizeToolResult(result: ExecutableToolResult): ToolResult {
908921
return base;
909922
}
910923

911-
function toolTelemetryOutcome(result: ToolResult): 'success' | 'error' | 'cancelled' {
924+
function toolTelemetryOutcome(
925+
result: ToolResult,
926+
executionOutcome: ToolExecutionOutcome,
927+
): 'success' | 'error' | 'cancelled' {
928+
if (executionOutcome === 'aborted' || executionOutcome === 'cancelled') return 'cancelled';
912929
if (result.isError !== true) return 'success';
913-
const text = toolOutputText(result.output).toLowerCase();
914-
return text.includes('aborted') ||
915-
text.includes('cancelled') ||
916-
text.includes('manually interrupted')
917-
? 'cancelled'
918-
: 'error';
930+
return 'error';
919931
}
920932

921933
function toolTelemetryErrorType(outcome: 'success' | 'error' | 'cancelled'): 'cancelled' | 'error' {
922934
if (outcome === 'cancelled') return 'cancelled';
923935
return 'error';
924936
}
925937

926-
function toolOutputText(output: ToolResult['output']): string {
927-
if (typeof output === 'string') return output;
928-
return output
929-
.filter((part): part is Extract<ContentPart, { type: 'text' }> => part.type === 'text')
930-
.map((part) => part.text)
931-
.join('');
932-
}
933938

934939
function isMediaContentPart(part: ContentPart): boolean {
935940
return part.type === 'image_url' || part.type === 'audio_url' || part.type === 'video_url';

0 commit comments

Comments
 (0)