@adminforth/completion-adapter-openai-responses 1.0.0 → 1.0.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.
@@ -0,0 +1,14 @@
1
+ import { ChatOpenAI } from "@langchain/openai";
2
+ import type { AdapterOptions } from "./types.js";
3
+ export type AgentModelPurpose = "primary" | "summary";
4
+ export declare function createLangChainAgentSpec(params: {
5
+ options: AdapterOptions;
6
+ maxTokens: number;
7
+ purpose: AgentModelPurpose;
8
+ configuredBaseUrl?: string;
9
+ clientConfiguration?: Record<string, unknown>;
10
+ useComplitionApi: boolean;
11
+ }): {
12
+ model: ChatOpenAI<import("@langchain/openai").ChatOpenAICallOptions>;
13
+ middleware: import("langchain").AgentMiddleware<undefined, undefined, unknown, readonly (import("@langchain/core/tools").ClientTool | import("@langchain/core/tools").ServerTool)[]>[];
14
+ };
@@ -0,0 +1,114 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __rest = (this && this.__rest) || function (s, e) {
11
+ var t = {};
12
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
13
+ t[p] = s[p];
14
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
15
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
16
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
17
+ t[p[i]] = s[p[i]];
18
+ }
19
+ return t;
20
+ };
21
+ import { AIMessage } from "@langchain/core/messages";
22
+ import { ChatOpenAI } from "@langchain/openai";
23
+ import { createMiddleware } from "langchain";
24
+ function getAgentReasoningEffort(purpose) {
25
+ return purpose === "summary" ? "minimal" : "low";
26
+ }
27
+ function buildReasoningConfig(params) {
28
+ var _a;
29
+ return Object.assign({ summary: "auto", effort: params.effort }, ((_a = params.reasoning) !== null && _a !== void 0 ? _a : {}));
30
+ }
31
+ function getTurnKey(context) {
32
+ return `${context.sessionId}:${context.turnId}`;
33
+ }
34
+ function getResponseId(message) {
35
+ var _a;
36
+ const metadata = message.response_metadata;
37
+ return (_a = metadata === null || metadata === void 0 ? void 0 : metadata.id) !== null && _a !== void 0 ? _a : null;
38
+ }
39
+ function getPreviousResponseId(modelSettings) {
40
+ return modelSettings === null || modelSettings === void 0 ? void 0 : modelSettings.previous_response_id;
41
+ }
42
+ function getContinuationMessages(messages, previousResponseId) {
43
+ var _a;
44
+ let continuationStartIndex = null;
45
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
46
+ const message = messages[index];
47
+ if (AIMessage.isInstance(message) &&
48
+ ((_a = message.response_metadata) === null || _a === void 0 ? void 0 : _a.id) ===
49
+ previousResponseId) {
50
+ continuationStartIndex = index + 1;
51
+ break;
52
+ }
53
+ }
54
+ if (continuationStartIndex === null) {
55
+ return null;
56
+ }
57
+ return messages.slice(continuationStartIndex);
58
+ }
59
+ function createOpenAiResponsesContinuationMiddleware() {
60
+ const responseIdsByTurn = new Map();
61
+ return createMiddleware({
62
+ name: "OpenAiResponsesContinuationMiddleware",
63
+ wrapModelCall(request, handler) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ var _a;
66
+ const context = request.runtime.context;
67
+ const turnKey = getTurnKey(context);
68
+ const previousResponseId = (_a = getPreviousResponseId(request.modelSettings)) !== null && _a !== void 0 ? _a : responseIdsByTurn.get(turnKey);
69
+ const continuationMessages = previousResponseId
70
+ ? getContinuationMessages(request.messages, previousResponseId)
71
+ : null;
72
+ const response = (yield handler(previousResponseId && continuationMessages
73
+ ? Object.assign(Object.assign({}, request), { messages: continuationMessages, modelSettings: Object.assign(Object.assign({}, request.modelSettings), { previous_response_id: previousResponseId }) }) : request));
74
+ const responseId = getResponseId(response);
75
+ if (responseId) {
76
+ responseIdsByTurn.set(turnKey, responseId);
77
+ }
78
+ else {
79
+ responseIdsByTurn.delete(turnKey);
80
+ }
81
+ return response;
82
+ });
83
+ },
84
+ });
85
+ }
86
+ export function createLangChainAgentSpec(params) {
87
+ const extraRequestBodyParameters = params.options.extraRequestBodyParameters || {};
88
+ const { reasoning } = extraRequestBodyParameters, modelKwargs = __rest(extraRequestBodyParameters, ["reasoning"]);
89
+ const normalizedModelKwargs = Object.assign({}, modelKwargs);
90
+ const chatOpenAiOptions = {
91
+ model: params.options.model || "gpt-5-nano",
92
+ apiKey: params.options.openAiApiKey,
93
+ maxTokens: params.maxTokens,
94
+ reasoning: buildReasoningConfig({
95
+ reasoning,
96
+ effort: getAgentReasoningEffort(params.purpose),
97
+ }),
98
+ modelKwargs: normalizedModelKwargs,
99
+ };
100
+ chatOpenAiOptions.useResponsesApi = !params.useComplitionApi;
101
+ let supportsResponseContinuation = true;
102
+ if (params.configuredBaseUrl || params.useComplitionApi) {
103
+ supportsResponseContinuation = false;
104
+ }
105
+ if (params.clientConfiguration) {
106
+ chatOpenAiOptions.configuration = params.clientConfiguration;
107
+ }
108
+ return {
109
+ model: new ChatOpenAI(chatOpenAiOptions),
110
+ middleware: params.purpose === "primary" && supportsResponseContinuation
111
+ ? [createOpenAiResponsesContinuationMiddleware()]
112
+ : [],
113
+ };
114
+ }
@@ -0,0 +1,50 @@
1
+ import type { CompletionStreamEvent, CompletionTool } from "adminforth";
2
+ import type { AdapterOptions } from "./types.js";
3
+ export type StreamChunkCallback = (chunk: string, event?: CompletionStreamEvent) => void | Promise<void>;
4
+ export type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
5
+ export type CompletionRequestInput = {
6
+ content: string;
7
+ maxTokens?: number;
8
+ outputSchema?: any;
9
+ reasoningEffort?: ReasoningEffort;
10
+ tools?: CompletionTool[];
11
+ onChunk?: StreamChunkCallback;
12
+ signal?: AbortSignal;
13
+ previousResponseId?: string;
14
+ };
15
+ export type UsedTokens = {
16
+ input_uncached: number;
17
+ input_cached: number;
18
+ output: number;
19
+ };
20
+ export type CompletionResult = {
21
+ content?: string;
22
+ finishReason?: string;
23
+ responseId?: string;
24
+ used_tokens?: UsedTokens;
25
+ error?: undefined;
26
+ } | {
27
+ error: string;
28
+ content?: string;
29
+ finishReason?: string;
30
+ responseId?: string;
31
+ used_tokens?: UsedTokens;
32
+ };
33
+ type FetchInput = Parameters<typeof fetch>[0];
34
+ type FetchInit = Parameters<typeof fetch>[1];
35
+ export declare class OpenAIResponsesService {
36
+ private options;
37
+ private client;
38
+ constructor(options: AdapterOptions);
39
+ getClientConfiguration(): {
40
+ fetch?: ((input: FetchInput, init?: FetchInit) => Promise<Response>) | undefined;
41
+ baseURL?: string | undefined;
42
+ } | undefined;
43
+ complete(request: CompletionRequestInput, signal: AbortSignal): Promise<CompletionResult>;
44
+ private getClient;
45
+ private createResponsesDebugFetch;
46
+ private getFetchUrl;
47
+ private isResponsesUrl;
48
+ private dumpRawRequest;
49
+ }
50
+ export {};
package/dist/openai.js ADDED
@@ -0,0 +1,349 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __rest = (this && this.__rest) || function (s, e) {
11
+ var t = {};
12
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
13
+ t[p] = s[p];
14
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
15
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
16
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
17
+ t[p[i]] = s[p[i]];
18
+ }
19
+ return t;
20
+ };
21
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
22
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
23
+ var m = o[Symbol.asyncIterator], i;
24
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
25
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
26
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
27
+ };
28
+ import OpenAI from "openai";
29
+ const RAW_REQUEST_LOG_PREFIX = "[CompletionAdapterOpenAIResponses] Raw /responses request";
30
+ function extractOutputText(data) {
31
+ return data.output_text || "";
32
+ }
33
+ function extractFunctionCall(data) {
34
+ var _a;
35
+ for (const item of (_a = data.output) !== null && _a !== void 0 ? _a : []) {
36
+ if (item.type === "function_call") {
37
+ return item;
38
+ }
39
+ }
40
+ return undefined;
41
+ }
42
+ function extractUsedTokens(data) {
43
+ var _a, _b;
44
+ const usage = data.usage;
45
+ if (!usage) {
46
+ return undefined;
47
+ }
48
+ const inputCached = (_b = (_a = usage.input_tokens_details) === null || _a === void 0 ? void 0 : _a.cached_tokens) !== null && _b !== void 0 ? _b : 0;
49
+ return {
50
+ input_uncached: Math.max(usage.input_tokens - inputCached, 0),
51
+ input_cached: inputCached,
52
+ output: usage.output_tokens,
53
+ };
54
+ }
55
+ function executeToolCall(toolCall, tools) {
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ const tool = tools === null || tools === void 0 ? void 0 : tools.find((candidate) => candidate.name === toolCall.name);
58
+ if (!tool) {
59
+ throw new Error(`Tool "${toolCall.name}" not found`);
60
+ }
61
+ const toolResult = yield tool.handler(JSON.parse(toolCall.arguments));
62
+ if (typeof toolResult === "string")
63
+ return toolResult;
64
+ if (typeof toolResult === "undefined")
65
+ return "";
66
+ return JSON.stringify(toolResult);
67
+ });
68
+ }
69
+ function resolveToolCallResult(params) {
70
+ return __awaiter(this, void 0, void 0, function* () {
71
+ var _a;
72
+ const toolCall = extractFunctionCall(params.response);
73
+ if (!toolCall) {
74
+ return null;
75
+ }
76
+ try {
77
+ const toolResult = yield executeToolCall(toolCall, params.tools);
78
+ if (typeof params.currentContent === "string" && toolResult) {
79
+ const delta = toolResult.startsWith(params.currentContent)
80
+ ? toolResult.slice(params.currentContent.length)
81
+ : toolResult;
82
+ if (delta) {
83
+ yield ((_a = params.onChunk) === null || _a === void 0 ? void 0 : _a.call(params, delta, {
84
+ type: "output",
85
+ delta,
86
+ text: toolResult,
87
+ }));
88
+ }
89
+ }
90
+ return {
91
+ content: toolResult,
92
+ finishReason: "tool_call",
93
+ responseId: params.response.id,
94
+ used_tokens: params.usedTokens,
95
+ };
96
+ }
97
+ catch (error) {
98
+ return {
99
+ error: (error === null || error === void 0 ? void 0 : error.message) || "Tool execution failed",
100
+ content: params.currentContent || undefined,
101
+ finishReason: "tool_call",
102
+ responseId: params.response.id,
103
+ used_tokens: params.usedTokens,
104
+ };
105
+ }
106
+ });
107
+ }
108
+ function handleCompletedResponse(params) {
109
+ return __awaiter(this, void 0, void 0, function* () {
110
+ var _a;
111
+ const usedTokens = extractUsedTokens(params.response);
112
+ const toolCallResult = yield resolveToolCallResult({
113
+ response: params.response,
114
+ tools: params.tools,
115
+ usedTokens,
116
+ });
117
+ if (toolCallResult) {
118
+ return toolCallResult;
119
+ }
120
+ return {
121
+ content: extractOutputText(params.response),
122
+ finishReason: ((_a = params.response.incomplete_details) === null || _a === void 0 ? void 0 : _a.reason)
123
+ ? params.response.incomplete_details.reason
124
+ : undefined,
125
+ responseId: params.response.id,
126
+ used_tokens: usedTokens,
127
+ };
128
+ });
129
+ }
130
+ function buildReasoningConfig(params) {
131
+ return Object.assign({ summary: "auto", effort: params.effort }, params.reasoning);
132
+ }
133
+ function splitExtraRequestBodyParameters(extra) {
134
+ const { reasoning } = extra, bodyParameters = __rest(extra, ["reasoning"]);
135
+ return {
136
+ reasoning,
137
+ bodyParameters,
138
+ };
139
+ }
140
+ function mapTools(tools) {
141
+ if (!(tools === null || tools === void 0 ? void 0 : tools.length)) {
142
+ return undefined;
143
+ }
144
+ return tools.map((tool) => ({
145
+ type: "function",
146
+ name: tool.name,
147
+ description: tool.description,
148
+ parameters: tool.input_schema,
149
+ strict: false,
150
+ }));
151
+ }
152
+ function buildResponseBody(params) {
153
+ var _a;
154
+ const { content, maxTokens: requestMaxTokens = 50, outputSchema: requestOutputSchema, reasoningEffort: requestReasoningEffort = "low", tools, } = params.request;
155
+ const { reasoning: extraReasoning, bodyParameters: extraBodyParameters, } = splitExtraRequestBodyParameters((_a = params.options.extraRequestBodyParameters) !== null && _a !== void 0 ? _a : {});
156
+ return Object.assign(Object.assign(Object.assign({}, extraBodyParameters), { model: params.options.model || "gpt-5-nano", input: content, max_output_tokens: requestMaxTokens, text: requestOutputSchema
157
+ ? {
158
+ format: Object.assign({ type: "json_schema" }, requestOutputSchema),
159
+ }
160
+ : {
161
+ format: {
162
+ type: "text",
163
+ },
164
+ }, reasoning: buildReasoningConfig({
165
+ reasoning: extraReasoning,
166
+ effort: requestReasoningEffort,
167
+ }), tools: mapTools(tools) }), (params.request.previousResponseId
168
+ ? { previous_response_id: params.request.previousResponseId }
169
+ : {}));
170
+ }
171
+ export class OpenAIResponsesService {
172
+ constructor(options) {
173
+ this.options = options;
174
+ this.client = null;
175
+ }
176
+ getClientConfiguration() {
177
+ const configuredBaseUrl = this.options.baseUrl;
178
+ const debugFetch = this.options.dumpRawRequest === true
179
+ ? this.createResponsesDebugFetch()
180
+ : undefined;
181
+ if (!configuredBaseUrl && !debugFetch) {
182
+ return undefined;
183
+ }
184
+ return Object.assign(Object.assign({}, (configuredBaseUrl ? { baseURL: configuredBaseUrl } : {})), (debugFetch ? { fetch: debugFetch } : {}));
185
+ }
186
+ complete(request, signal) {
187
+ return __awaiter(this, void 0, void 0, function* () {
188
+ var _a, e_1, _b, _c;
189
+ const { tools, onChunk: streamChunkCallback } = request;
190
+ const isStreaming = typeof streamChunkCallback === "function";
191
+ const body = buildResponseBody({
192
+ options: this.options,
193
+ request,
194
+ });
195
+ let fullContent = "";
196
+ let fullReasoning = "";
197
+ let finishReason;
198
+ let completedResponse;
199
+ let usedTokens;
200
+ const handleStreamEvent = (event) => __awaiter(this, void 0, void 0, function* () {
201
+ var _a, _b;
202
+ switch (event.type) {
203
+ case "response.output_text.delta": {
204
+ const delta = event.delta || "";
205
+ if (!delta)
206
+ return;
207
+ fullContent += delta;
208
+ yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
209
+ type: "output",
210
+ delta,
211
+ text: fullContent,
212
+ }));
213
+ return;
214
+ }
215
+ case "response.reasoning_summary_text.delta":
216
+ case "response.reasoning_text.delta": {
217
+ const delta = event.delta || "";
218
+ if (!delta)
219
+ return;
220
+ fullReasoning += delta;
221
+ yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
222
+ type: "reasoning",
223
+ delta,
224
+ text: fullReasoning,
225
+ }));
226
+ return;
227
+ }
228
+ case "response.completed":
229
+ case "response.incomplete": {
230
+ const response = event.response;
231
+ finishReason =
232
+ ((_a = response.incomplete_details) === null || _a === void 0 ? void 0 : _a.reason) || response.status || finishReason;
233
+ completedResponse = response;
234
+ usedTokens = extractUsedTokens(response);
235
+ return;
236
+ }
237
+ case "response.failed":
238
+ throw new Error(((_b = event.response.error) === null || _b === void 0 ? void 0 : _b.message) || "Response failed");
239
+ case "error":
240
+ throw new Error(event.message || "Response failed");
241
+ }
242
+ });
243
+ try {
244
+ if (!isStreaming) {
245
+ const params = Object.assign(Object.assign({}, body), { stream: false });
246
+ const data = yield this.getClient().responses.create(params, { signal });
247
+ return handleCompletedResponse({ response: data, tools });
248
+ }
249
+ const params = Object.assign(Object.assign({}, body), { stream: true });
250
+ const stream = yield this.getClient().responses.create(params, { signal });
251
+ try {
252
+ for (var _d = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a; _d = true) {
253
+ _c = stream_1_1.value;
254
+ _d = false;
255
+ const event = _c;
256
+ yield handleStreamEvent(event);
257
+ }
258
+ }
259
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
260
+ finally {
261
+ try {
262
+ if (!_d && !_a && (_b = stream_1.return)) yield _b.call(stream_1);
263
+ }
264
+ finally { if (e_1) throw e_1.error; }
265
+ }
266
+ if (completedResponse) {
267
+ const toolCallResult = yield resolveToolCallResult({
268
+ response: completedResponse,
269
+ tools,
270
+ currentContent: fullContent,
271
+ onChunk: streamChunkCallback,
272
+ usedTokens,
273
+ });
274
+ if (toolCallResult) {
275
+ return toolCallResult;
276
+ }
277
+ }
278
+ return {
279
+ content: fullContent || undefined,
280
+ finishReason,
281
+ responseId: completedResponse === null || completedResponse === void 0 ? void 0 : completedResponse.id,
282
+ used_tokens: usedTokens,
283
+ };
284
+ }
285
+ catch (error) {
286
+ if (signal.aborted) {
287
+ return {
288
+ error: (error === null || error === void 0 ? void 0 : error.message) || "Generation aborted",
289
+ content: fullContent || undefined,
290
+ finishReason: "aborted",
291
+ used_tokens: usedTokens,
292
+ };
293
+ }
294
+ if (isStreaming) {
295
+ return {
296
+ error: (error === null || error === void 0 ? void 0 : error.message) || "Streaming failed",
297
+ content: fullContent || undefined,
298
+ finishReason,
299
+ used_tokens: usedTokens,
300
+ };
301
+ }
302
+ return {
303
+ error: (error === null || error === void 0 ? void 0 : error.message) || "OpenAI request failed",
304
+ };
305
+ }
306
+ });
307
+ }
308
+ getClient() {
309
+ if (!this.client) {
310
+ this.client = new OpenAI(Object.assign({ apiKey: this.options.openAiApiKey }, this.getClientConfiguration()));
311
+ }
312
+ return this.client;
313
+ }
314
+ createResponsesDebugFetch() {
315
+ return (input, init) => __awaiter(this, void 0, void 0, function* () {
316
+ const url = this.getFetchUrl(input);
317
+ if (this.isResponsesUrl(url) && typeof (init === null || init === void 0 ? void 0 : init.body) === "string") {
318
+ this.dumpRawRequest(url, init.body);
319
+ }
320
+ return fetch(input, init);
321
+ });
322
+ }
323
+ getFetchUrl(input) {
324
+ if (typeof input === "string") {
325
+ return input;
326
+ }
327
+ if (input instanceof URL) {
328
+ return input.toString();
329
+ }
330
+ return input.url;
331
+ }
332
+ isResponsesUrl(url) {
333
+ try {
334
+ return new URL(url).pathname.endsWith("/responses");
335
+ }
336
+ catch (_a) {
337
+ return url.endsWith("/responses") || url.includes("/responses?");
338
+ }
339
+ }
340
+ dumpRawRequest(url, body) {
341
+ console.info(`${RAW_REQUEST_LOG_PREFIX} ${url}`);
342
+ try {
343
+ console.info(JSON.stringify(JSON.parse(body), null, 2));
344
+ }
345
+ catch (_a) {
346
+ console.info(body);
347
+ }
348
+ }
349
+ }
package/dist/types.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type OpenAI from "openai";
2
+ export type AdapterExtraRequestBodyParameters = Partial<Omit<OpenAI.Responses.ResponseCreateParamsNonStreaming, "stream">>;
1
3
  export interface AdapterOptions {
2
4
  /**
3
5
  * OpenAI API key. Go to https://platform.openai.com/, go to Dashboard -> API keys -> Create new secret key
@@ -28,7 +30,7 @@ export interface AdapterOptions {
28
30
  /**
29
31
  * Additional request body parameters to include in the API request.
30
32
  */
31
- extraRequestBodyParameters?: Record<string, unknown>;
33
+ extraRequestBodyParameters?: AdapterExtraRequestBodyParameters;
32
34
  /**
33
35
  * Logs the exact JSON body sent to the OpenAI Responses endpoint.
34
36
  * Authorization headers are not logged.