ambitry 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db.d.ts +11 -0
- package/dist/db.js +17 -1
- package/dist/viewer.js +19 -2
- package/package.json +1 -1
package/dist/db.d.ts
CHANGED
|
@@ -14,6 +14,9 @@ export interface TraceRow {
|
|
|
14
14
|
request_json: string;
|
|
15
15
|
response_json: string | null;
|
|
16
16
|
error: string | null;
|
|
17
|
+
/** Present on recentTraces only: comma-separated tool names, and denials. */
|
|
18
|
+
tool_names?: string | null;
|
|
19
|
+
denied_count?: number;
|
|
17
20
|
}
|
|
18
21
|
export interface ToolCallRow {
|
|
19
22
|
id: string;
|
|
@@ -85,6 +88,14 @@ export declare class Store {
|
|
|
85
88
|
decision: Decision;
|
|
86
89
|
started_at: number;
|
|
87
90
|
}[];
|
|
91
|
+
/**
|
|
92
|
+
* Recent traces, each carrying a summary of its tool calls.
|
|
93
|
+
*
|
|
94
|
+
* The counts are joined here rather than derived client-side because a
|
|
95
|
+
* denied tool call returns HTTP 200 with the call stripped — so anything
|
|
96
|
+
* counting blocks by status code reports zero while enforcement is
|
|
97
|
+
* actively working.
|
|
98
|
+
*/
|
|
88
99
|
recentTraces(limit?: number): TraceRow[];
|
|
89
100
|
trace(id: string): {
|
|
90
101
|
trace: TraceRow;
|
package/dist/db.js
CHANGED
|
@@ -117,9 +117,25 @@ export class Store {
|
|
|
117
117
|
order by t.started_at asc`)
|
|
118
118
|
.all(since);
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Recent traces, each carrying a summary of its tool calls.
|
|
122
|
+
*
|
|
123
|
+
* The counts are joined here rather than derived client-side because a
|
|
124
|
+
* denied tool call returns HTTP 200 with the call stripped — so anything
|
|
125
|
+
* counting blocks by status code reports zero while enforcement is
|
|
126
|
+
* actively working.
|
|
127
|
+
*/
|
|
120
128
|
recentTraces(limit = 100) {
|
|
121
129
|
return this.db
|
|
122
|
-
.prepare(`select
|
|
130
|
+
.prepare(`select t.*,
|
|
131
|
+
-- decision:name pairs, because group_concat order is not defined
|
|
132
|
+
-- and the client must know which specific call was denied.
|
|
133
|
+
(select group_concat(c.decision || ':' || c.name, ',')
|
|
134
|
+
from tool_calls c where c.trace_id = t.id) as tool_names,
|
|
135
|
+
(select count(*) from tool_calls c where c.trace_id = t.id and c.decision = 'deny') as denied_count
|
|
136
|
+
from traces t
|
|
137
|
+
order by t.started_at desc
|
|
138
|
+
limit ?`)
|
|
123
139
|
.all(limit);
|
|
124
140
|
}
|
|
125
141
|
trace(id) {
|
package/dist/viewer.js
CHANGED
|
@@ -90,6 +90,19 @@ const $ = (id) => document.getElementById(id);
|
|
|
90
90
|
const money = (n) => n == null ? '—' : '$' + Number(n).toFixed(n < 1 ? 4 : 2);
|
|
91
91
|
const esc = (s) => String(s).replace(/[&<>]/g, (c) => ({'&':'&','<':'<','>':'>'}[c]));
|
|
92
92
|
|
|
93
|
+
// Tool names for a row, with denied ones marked. The whole point of the
|
|
94
|
+
// product is visible here, so an empty cell is a wasted line.
|
|
95
|
+
function toolCells(t) {
|
|
96
|
+
const pairs = (t.tool_names || '').split(',').filter(Boolean);
|
|
97
|
+
if (!pairs.length) return '<span class="muted">' + (t.streamed ? 'stream' : '—') + '</span>';
|
|
98
|
+
return pairs.map(p => {
|
|
99
|
+
const cut = p.indexOf(':');
|
|
100
|
+
const decision = p.slice(0, cut);
|
|
101
|
+
const name = p.slice(cut + 1);
|
|
102
|
+
return '<span class="tag ' + (decision === 'deny' ? 'no' : 'ok') + '">' + esc(name) + '</span>';
|
|
103
|
+
}).join('');
|
|
104
|
+
}
|
|
105
|
+
|
|
93
106
|
function meter(label, spent, cap) {
|
|
94
107
|
const pct = cap ? Math.min(100, (spent / cap) * 100) : 0;
|
|
95
108
|
const bar = cap
|
|
@@ -122,7 +135,11 @@ async function refresh() {
|
|
|
122
135
|
'<span class="mono">' + x.occurrences + '× in ' + x.traces + ' request' + (x.traces === 1 ? '' : 's') +
|
|
123
136
|
'</span></div>').join('') + '</div>';
|
|
124
137
|
|
|
125
|
-
|
|
138
|
+
// Counts denied tool calls as well as rejected requests. Tool denial
|
|
139
|
+
// returns 200 with the call stripped, so counting status codes alone
|
|
140
|
+
// reports zero blocks while enforcement is visibly working.
|
|
141
|
+
const blocked = traces.reduce((n, t) =>
|
|
142
|
+
n + (t.denied_count || 0) + (t.status === 403 ? 1 : 0), 0);
|
|
126
143
|
$('cards').innerHTML =
|
|
127
144
|
meter('Spend · last hour', status.spend.lastHour, status.limits.perHourUsd) +
|
|
128
145
|
meter('Spend · last day', status.spend.lastDay, status.limits.perDayUsd) +
|
|
@@ -143,7 +160,7 @@ async function refresh() {
|
|
|
143
160
|
'<td>' + new Date(t.started_at).toLocaleTimeString() +
|
|
144
161
|
(t.status === 403 ? ' <span class="tag no">blocked</span>' : '') + '</td>' +
|
|
145
162
|
'<td>' + esc(t.model || '—') + '</td>' +
|
|
146
|
-
'<td
|
|
163
|
+
'<td>' + toolCells(t) + '</td>' +
|
|
147
164
|
'<td class="num">' + ((t.input_tokens ?? 0) + (t.output_tokens ?? 0) || '—') + '</td>' +
|
|
148
165
|
'<td class="num">' + money(t.cost_usd) + '</td></tr>').join('') +
|
|
149
166
|
'</tbody></table>';
|