From 3e56c26bdaf7b55a9d35398ca2a18e986334edb4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Jul 2026 04:13:24 +0000
Subject: [PATCH 1/8] Initial plan
From 3442d688fb91b8844f11704ebf8bf35c12ae7288 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Jul 2026 04:18:03 +0000
Subject: [PATCH 2/8] Add OpenAI Responses API (JSON Schema mode) support
---
js/owrap.ai.js | 100 ++++++++++++++++++++++++++
tests/autoTestAll.AI.js | 146 ++++++++++++++++++++++++++++++++++++++
tests/autoTestAll.AI.yaml | 66 ++++++++++++-----
3 files changed, 294 insertions(+), 18 deletions(-)
diff --git a/js/owrap.ai.js b/js/owrap.ai.js
index e922f509..b5332ac8 100644
--- a/js/owrap.ai.js
+++ b/js/owrap.ai.js
@@ -698,6 +698,51 @@ OpenWrap.ai.prototype.__gpttypes = {
if (isMap(res) && isDef(res.error)) return res
return res.content
},
+ rawResponse: (aPrompt, aModel, aTemperature, aJsonSchemaFlag, aTools, aResponseSchema) => {
+ aPrompt = _$(aPrompt, "aPrompt").default(__)
+ aTemperature = _$(aTemperature, "aTemperature").isNumber().default(_temperature)
+ aModel = _$(aModel, "aModel").isString().default(_model)
+ aJsonSchemaFlag = _$(aJsonSchemaFlag, "aJsonSchemaFlag").isBoolean().default(false)
+ if (isUnDef(aTools)) {
+ aTools = Object.keys(_r.tools)
+ } else if (isMap(aTools)) {
+ aTools = Object.keys(aTools)
+ }
+ aTools = _$(aTools, "aTools").isArray().default([])
+
+ _resetStats()
+ var msgs = []
+ if (isString(aPrompt)) aPrompt = [ aPrompt ]
+ aPrompt = _r.conversation.concat(aPrompt)
+ msgs = aPrompt.filter(c => isDef(c)).map(c => isMap(c) ? c : { role: "user", content: c })
+
+ _r.conversation = msgs
+ if (_noSystem) msgs = msgs.map(m => { if (m.role == "system") m.role = "developer"; return m })
+ var body = {
+ model: aModel,
+ temperature: aTemperature,
+ messages: msgs
+ }
+
+ if (isDef(aResponseSchema) && isMap(aResponseSchema)) {
+ body.response_format = {
+ type: "json_schema",
+ json_schema: {
+ name: aResponseSchema.name || "Response",
+ description: aResponseSchema.description || "API Response",
+ schema: aResponseSchema.schema || {},
+ strict: aResponseSchema.strict !== false
+ }
+ }
+ }
+
+ body = merge(body, aOptions.params)
+ if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'client'}, merge({_t:nowNano(),_f:'client'}, body))
+ var _res = _r._request(_route("chat/completions", aModel), body)
+ if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'llm'}, merge({_t:nowNano(),_f:'llm'}, _res))
+ _captureStats(_res, body)
+ return _res
+ },
rawImgGen: (aPrompt, aModel) => {
aPrompt = _$(aPrompt, "aPrompt").default(__)
aModel = _$(aModel, "aModel").isString().default(_model)
@@ -3573,6 +3618,39 @@ OpenWrap.ai.prototype.gpt.prototype.rawPromptWithStats = function(aPrompt, aRole
return { response: response, stats: this.getLastStats() }
}
+/**
+ *
+ * ow.ai.gpt.jsonSchemaPrompt(aPrompt, aResponseSchema, aModel, aTemperature, tools) : Object
+ * Executes a prompt using OpenAI's JSON Schema response format, enforcing structured output validated against aResponseSchema.\
+ * aResponseSchema should be a map with: name (string), description (string), schema (JSON Schema object), strict (boolean, defaults to true).\
+ * Returns the parsed JSON response matching the provided schema.\
+ * Only supported by the "openai" provider; throws if the underlying provider does not implement rawResponse.
+ *
+ */
+OpenWrap.ai.prototype.gpt.prototype.jsonSchemaPrompt = function(aPrompt, aResponseSchema, aModel, aTemperature, tools) {
+ if (!isFunction(this.model.rawResponse)) throw "JSON Schema responses not supported by this provider"
+ var response = this.model.rawResponse(aPrompt, aModel, aTemperature, false, tools, aResponseSchema)
+ if (isArray(response.choices) && response.choices.length > 0) {
+ if (response.choices[0].finish_reason == "stop") {
+ var content = response.choices[0].message.content
+ return isString(content) ? jsonParse(content, __, __, true) : content
+ }
+ }
+ return response
+}
+
+/**
+ *
+ * ow.ai.gpt.jsonSchemaPromptWithStats(aPrompt, aResponseSchema, aModel, aTemperature, tools) : Map
+ * Executes jsonSchemaPrompt and returns the parsed response together with any reported statistics ({ response, stats }).\
+ * aResponseSchema should be a map with: name (string), description (string), schema (JSON Schema object), strict (boolean, defaults to true).
+ *
+ */
+OpenWrap.ai.prototype.gpt.prototype.jsonSchemaPromptWithStats = function(aPrompt, aResponseSchema, aModel, aTemperature, tools) {
+ var response = this.jsonSchemaPrompt(aPrompt, aResponseSchema, aModel, aTemperature, tools)
+ return { response: response, stats: this.getLastStats() }
+}
+
/**
*
* ow.ai.gpt.promptImgGen(aPrompt, aModel, anOutputPathPrefix) : Array
@@ -4380,6 +4458,28 @@ global.$gpt = function(aModel) {
*
*/
importConversation: (aExport) => { _g.importConversation(aExport); return _r },
+ /**
+ *
+ * $gpt.jsonSchemaPrompt(aPrompt, aResponseSchema, aModel, aTemperature, tools) : Object
+ * Executes a prompt using OpenAI's JSON Schema response format, enforcing structured output validated against aResponseSchema.\
+ * aResponseSchema should be a map with: name (string), description (string), schema (JSON Schema object), strict (boolean, defaults to true).\
+ * Returns the parsed JSON response matching the provided schema.\
+ * Only supported by the "openai" provider.
+ *
+ */
+ jsonSchemaPrompt: (aPrompt, aResponseSchema, aModel, aTemperature, tools) => {
+ return _g.jsonSchemaPrompt(aPrompt, aResponseSchema, aModel, aTemperature, tools)
+ },
+ /**
+ *
+ * $gpt.jsonSchemaPromptWithStats(aPrompt, aResponseSchema, aModel, aTemperature, tools) : Map
+ * Executes jsonSchemaPrompt and returns the parsed response together with any reported statistics ({ response, stats }).\
+ * aResponseSchema should be a map with: name (string), description (string), schema (JSON Schema object), strict (boolean, defaults to true).
+ *
+ */
+ jsonSchemaPromptWithStats: (aPrompt, aResponseSchema, aModel, aTemperature, tools) => {
+ return _g.jsonSchemaPromptWithStats(aPrompt, aResponseSchema, aModel, aTemperature, tools)
+ },
/**
*
* $gpt.close()
diff --git a/tests/autoTestAll.AI.js b/tests/autoTestAll.AI.js
index 5e593b20..af8d2b3c 100644
--- a/tests/autoTestAll.AI.js
+++ b/tests/autoTestAll.AI.js
@@ -815,4 +815,150 @@
ow.test.assert(deltas, [ "part-1", "part-2" ], "Problem preserving Ollama streaming deltas after warmup.");
ow.test.assert(res.content, "part-1part-2", "Problem aggregating Ollama streaming content after warmup.");
};
+
+ exports.testAIOpenAIRawResponse = function() {
+ ow.loadAI();
+
+ var g = new ow.ai.gpt("openai", { key: "test-key", model: "gpt-4o" });
+ var requests = [];
+ var schema = {
+ name: "PersonInfo",
+ description: "Extract person details",
+ schema: {
+ type: "object",
+ properties: {
+ name: { type: "string" },
+ age: { type: "number" }
+ },
+ required: ["name", "age"],
+ additionalProperties: false
+ },
+ strict: true
+ };
+
+ g.model._request = function(url, body) {
+ requests.push({ url: url, body: __cloneForTest(body) });
+ return {
+ model: "gpt-4o",
+ choices: [
+ {
+ finish_reason: "stop",
+ message: { role: "assistant", content: '{"name":"Alice","age":30}' }
+ }
+ ],
+ usage: { prompt_tokens: 20, completion_tokens: 10, total_tokens: 30 }
+ };
+ };
+
+ var rawRes = g.model.rawResponse("Extract: Alice, 30", "gpt-4o", 0.5, false, [], schema);
+ ow.test.assert(requests.length, 1, "Problem: rawResponse should make exactly one request.");
+ ow.test.assert(requests[0].url.indexOf("chat/completions") >= 0, true, "Problem: rawResponse should use chat/completions endpoint.");
+ ow.test.assert(requests[0].body.response_format.type, "json_schema", "Problem: rawResponse should set response_format.type to json_schema.");
+ ow.test.assert(requests[0].body.response_format.json_schema.name, "PersonInfo", "Problem: rawResponse should pass schema name.");
+ ow.test.assert(requests[0].body.response_format.json_schema.strict, true, "Problem: rawResponse should pass strict flag.");
+ ow.test.assert(isArray(rawRes.choices) && rawRes.choices.length > 0, true, "Problem: rawResponse should return choices array.");
+ };
+
+ exports.testAIOpenAIJsonSchemaPrompt = function() {
+ ow.loadAI();
+
+ var g = new ow.ai.gpt("openai", { key: "test-key", model: "gpt-4o" });
+ var schema = {
+ name: "PersonInfo",
+ description: "Extract person details",
+ schema: {
+ type: "object",
+ properties: {
+ name: { type: "string" },
+ age: { type: "number" }
+ },
+ required: ["name", "age"],
+ additionalProperties: false
+ },
+ strict: true
+ };
+
+ g.model._request = function(url, body) {
+ return {
+ model: "gpt-4o",
+ choices: [
+ {
+ finish_reason: "stop",
+ message: { role: "assistant", content: '{"name":"Alice","age":30}' }
+ }
+ ],
+ usage: { prompt_tokens: 20, completion_tokens: 10, total_tokens: 30 }
+ };
+ };
+
+ var result = g.jsonSchemaPrompt("Extract: Alice, 30", schema, "gpt-4o", 0.5);
+ ow.test.assert(isMap(result), true, "Problem: jsonSchemaPrompt should return a parsed map.");
+ ow.test.assert(result.name, "Alice", "Problem: jsonSchemaPrompt should parse name from JSON.");
+ ow.test.assert(result.age, 30, "Problem: jsonSchemaPrompt should parse age from JSON.");
+ };
+
+ exports.testAIOpenAIJsonSchemaPromptWithStats = function() {
+ ow.loadAI();
+
+ var g = new ow.ai.gpt("openai", { key: "test-key", model: "gpt-4o" });
+ var schema = {
+ name: "PersonInfo",
+ schema: { type: "object", properties: { name: { type: "string" } }, required: ["name"], additionalProperties: false },
+ strict: true
+ };
+
+ g.model._request = function(url, body) {
+ return {
+ model: "gpt-4o",
+ choices: [
+ {
+ finish_reason: "stop",
+ message: { role: "assistant", content: '{"name":"Bob"}' }
+ }
+ ],
+ usage: { prompt_tokens: 15, completion_tokens: 8, total_tokens: 23 }
+ };
+ };
+
+ var result = g.jsonSchemaPromptWithStats("Get name: Bob", schema);
+ ow.test.assert(isMap(result), true, "Problem: jsonSchemaPromptWithStats should return a map.");
+ ow.test.assert(isDef(result.response), true, "Problem: jsonSchemaPromptWithStats should have response field.");
+ ow.test.assert(isDef(result.stats), true, "Problem: jsonSchemaPromptWithStats should have stats field.");
+ ow.test.assert(result.response.name, "Bob", "Problem: jsonSchemaPromptWithStats response should contain parsed name.");
+ ow.test.assert(result.stats.tokens.total, 23, "Problem: jsonSchemaPromptWithStats stats should contain token usage.");
+ };
+
+ exports.testAIOpenAIJsonSchemaPromptSchemaDefault = function() {
+ ow.loadAI();
+
+ var g = new ow.ai.gpt("openai", { key: "test-key", model: "gpt-4o" });
+ var requests = [];
+
+ g.model._request = function(url, body) {
+ requests.push(__cloneForTest(body));
+ return {
+ model: "gpt-4o",
+ choices: [ { finish_reason: "stop", message: { role: "assistant", content: '{"val":1}' } } ],
+ usage: { prompt_tokens: 5, completion_tokens: 5, total_tokens: 10 }
+ };
+ };
+
+ // Schema without description or strict – should use defaults
+ g.model.rawResponse("test", "gpt-4o", 0.5, false, [], { name: "MySchema", schema: { type: "object", additionalProperties: false } });
+ ow.test.assert(requests[0].response_format.json_schema.description, "API Response", "Problem: rawResponse should default description to 'API Response'.");
+ ow.test.assert(requests[0].response_format.json_schema.strict, true, "Problem: rawResponse should default strict to true.");
+ };
+
+ exports.testAIOpenAIJsonSchemaPromptUnsupportedProvider = function() {
+ ow.loadAI();
+
+ var g = new ow.ai.gpt("gemini", { key: "test-key" });
+ var threw = false;
+ try {
+ g.jsonSchemaPrompt("test", { name: "T", schema: {} });
+ } catch(e) {
+ threw = true;
+ }
+ ow.test.assert(threw, true, "Problem: jsonSchemaPrompt should throw for providers that don't support rawResponse.");
+ };
})();
diff --git a/tests/autoTestAll.AI.yaml b/tests/autoTestAll.AI.yaml
index f7e2ec2b..1421e0e1 100644
--- a/tests/autoTestAll.AI.yaml
+++ b/tests/autoTestAll.AI.yaml
@@ -120,10 +120,35 @@ jobs:
to : oJob Test
exec: args.func = args.tests.testAIOllamaStreamingToolExecutionAndConversationIds;
- - name: AI::Ollama streaming warmup on fresh conversation
- from: AI::Init
- to : oJob Test
- exec: args.func = args.tests.testAIOllamaStreamingWarmsModelOnFreshConversation;
+ - name: AI::Ollama streaming warmup on fresh conversation
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOllamaStreamingWarmsModelOnFreshConversation;
+
+ - name: AI::OpenAI rawResponse method
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIRawResponse;
+
+ - name: AI::OpenAI jsonSchemaPrompt
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIJsonSchemaPrompt;
+
+ - name: AI::OpenAI jsonSchemaPromptWithStats
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIJsonSchemaPromptWithStats;
+
+ - name: AI::OpenAI jsonSchemaPrompt schema defaults
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIJsonSchemaPromptSchemaDefault;
+
+ - name: AI::OpenAI jsonSchemaPrompt unsupported provider
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIJsonSchemaPromptUnsupportedProvider;
todo:
# ow.ai tests
@@ -137,17 +162,22 @@ todo:
- AI::LSTM network save and load
- AI::Normalize with schema
- AI::Normalize features array
- - AI::Perceptron XOR save and load
- - AI::Test ID3
- - AI::Test C45
- - AI::Test KMeans
- - AI::GPT prompt argument routing
- - AI::OpenAI transport modes
- - AI::OpenAI stats capture includes cached and reasoning
- - AI::Anthropic prompt caching headers
- - AI::Anthropic prompt caching body and stats
- - AI::OpenAI tool recursion without duplication
- - AI::OpenAI streaming tool recursion without duplication
- - AI::OpenAI export conversation falls back to tool name
- - AI::Ollama streaming tool execution and ids
- - AI::Ollama streaming warmup on fresh conversation
+ - AI::Perceptron XOR save and load
+ - AI::Test ID3
+ - AI::Test C45
+ - AI::Test KMeans
+ - AI::GPT prompt argument routing
+ - AI::OpenAI transport modes
+ - AI::OpenAI stats capture includes cached and reasoning
+ - AI::Anthropic prompt caching headers
+ - AI::Anthropic prompt caching body and stats
+ - AI::OpenAI tool recursion without duplication
+ - AI::OpenAI streaming tool recursion without duplication
+ - AI::OpenAI export conversation falls back to tool name
+ - AI::Ollama streaming tool execution and ids
+ - AI::Ollama streaming warmup on fresh conversation
+ - AI::OpenAI rawResponse method
+ - AI::OpenAI jsonSchemaPrompt
+ - AI::OpenAI jsonSchemaPromptWithStats
+ - AI::OpenAI jsonSchemaPrompt schema defaults
+ - AI::OpenAI jsonSchemaPrompt unsupported provider
From bb3d97e2b8298ea8ea36031eb05485fc2743c95d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Jul 2026 04:20:00 +0000
Subject: [PATCH 3/8] Fix test formatting for consistency with existing test
style
---
tests/autoTestAll.AI.js | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/tests/autoTestAll.AI.js b/tests/autoTestAll.AI.js
index af8d2b3c..d86fc725 100644
--- a/tests/autoTestAll.AI.js
+++ b/tests/autoTestAll.AI.js
@@ -903,7 +903,14 @@
var g = new ow.ai.gpt("openai", { key: "test-key", model: "gpt-4o" });
var schema = {
name: "PersonInfo",
- schema: { type: "object", properties: { name: { type: "string" } }, required: ["name"], additionalProperties: false },
+ schema: {
+ type: "object",
+ properties: {
+ name: { type: "string" }
+ },
+ required: ["name"],
+ additionalProperties: false
+ },
strict: true
};
@@ -938,13 +945,21 @@
requests.push(__cloneForTest(body));
return {
model: "gpt-4o",
- choices: [ { finish_reason: "stop", message: { role: "assistant", content: '{"val":1}' } } ],
+ choices: [
+ {
+ finish_reason: "stop",
+ message: { role: "assistant", content: '{"val":1}' }
+ }
+ ],
usage: { prompt_tokens: 5, completion_tokens: 5, total_tokens: 10 }
};
};
// Schema without description or strict – should use defaults
- g.model.rawResponse("test", "gpt-4o", 0.5, false, [], { name: "MySchema", schema: { type: "object", additionalProperties: false } });
+ g.model.rawResponse("test", "gpt-4o", 0.5, false, [], {
+ name: "MySchema",
+ schema: { type: "object", additionalProperties: false }
+ });
ow.test.assert(requests[0].response_format.json_schema.description, "API Response", "Problem: rawResponse should default description to 'API Response'.");
ow.test.assert(requests[0].response_format.json_schema.strict, true, "Problem: rawResponse should default strict to true.");
};
From 46e07dd897fb91065e48fd171ac6987671e59623 Mon Sep 17 00:00:00 2001
From: nmaguiar
Date: Mon, 6 Jul 2026 14:23:12 +0100
Subject: [PATCH 4/8] feat(ai): add support for OpenAI responses API
Implement the /responses endpoint integration in owrap.ai.js, including response schema normalization, input text conversion, and error handling. Update AI tests to verify the new responses endpoint usage and JSON schema formatting.
---
js/owrap.ai.js | 159 ++++++++++++++++++++++++++----
tests/autoTestAll.AI.js | 200 ++++++++++++++++++++++++++++++++------
tests/autoTestAll.AI.yaml | 52 ++++++----
3 files changed, 346 insertions(+), 65 deletions(-)
diff --git a/js/owrap.ai.js b/js/owrap.ai.js
index b5332ac8..584d03ab 100644
--- a/js/owrap.ai.js
+++ b/js/owrap.ai.js
@@ -189,6 +189,74 @@ OpenWrap.ai.prototype.__gpttypes = {
return merge(_h, aOptions.headers)
}
var _buildURL = aURI => _joinURL(aOptions.url, aURI)
+ var _responsesAvailable = __
+ var _normalizeResponseSchema = aResponseSchema => {
+ if (!isMap(aResponseSchema)) return __
+ return {
+ name : aResponseSchema.name || "Response",
+ description: aResponseSchema.description || "API Response",
+ schema : aResponseSchema.schema || {},
+ strict : aResponseSchema.strict !== false
+ }
+ }
+ var _toResponseInputText = aContent => {
+ if (isMap(aContent) || isArray(aContent)) return stringify(aContent, __, "")
+ return isDef(aContent) ? String(aContent) : ""
+ }
+ var _toResponsesInput = aMsgs => {
+ return _$(aMsgs, "aMsgs").isArray().default([]).map(m => {
+ if (!isMap(m)) return { role: "user", content: [ { type: "input_text", text: _toResponseInputText(m) } ] }
+ if (isArray(m.content)) {
+ return {
+ role : isString(m.role) ? m.role : "user",
+ content: clone(m.content)
+ }
+ }
+ return {
+ role : isString(m.role) ? m.role : "user",
+ content: [ { type: "input_text", text: _toResponseInputText(m.content) } ]
+ }
+ })
+ }
+ var _extractResponseErrorMessage = aResponse => {
+ if (!isMap(aResponse)) return ""
+ if (isMap(aResponse.error)) {
+ if (isString(aResponse.error.message)) return aResponse.error.message
+ if (isString(aResponse.error.error)) return aResponse.error.error
+ return stringify(aResponse.error, __, "")
+ }
+ if (isString(aResponse.message)) return aResponse.message
+ return stringify(aResponse, __, "")
+ }
+ var _isResponsesUnavailable = aResponse => {
+ if (!isMap(aResponse)) return false
+ var _status = isDef(aResponse.status) ? Number(aResponse.status) : (isMap(aResponse.error) && isDef(aResponse.error.status) ? Number(aResponse.error.status) : __)
+ if ([ 404, 405, 410, 501 ].indexOf(_status) >= 0) return true
+
+ var _code = (isMap(aResponse.error) && isDef(aResponse.error.code) ? aResponse.error.code : aResponse.code)
+ if (isDef(_code)) {
+ _code = String(_code).toLowerCase()
+ if (_code.indexOf("not_found") >= 0 || _code.indexOf("unsupported") >= 0 || _code.indexOf("unknown_url") >= 0 || _code.indexOf("unknown_endpoint") >= 0) return true
+ }
+
+ var _type = (isMap(aResponse.error) && isDef(aResponse.error.type) ? aResponse.error.type : aResponse.type)
+ if (isDef(_type)) {
+ _type = String(_type).toLowerCase()
+ if (_type.indexOf("not_found") >= 0 || _type.indexOf("unsupported") >= 0) return true
+ }
+
+ var _msg = _extractResponseErrorMessage(aResponse).toLowerCase()
+ return (
+ _msg.indexOf("/responses") >= 0 && (
+ _msg.indexOf("not found") >= 0 ||
+ _msg.indexOf("unsupported") >= 0 ||
+ _msg.indexOf("unknown") >= 0 ||
+ _msg.indexOf("not available") >= 0 ||
+ _msg.indexOf("unavailable") >= 0 ||
+ _msg.indexOf("does not exist") >= 0
+ )
+ ) || _msg.indexOf("unknown endpoint") >= 0 || _msg.indexOf("unknown url") >= 0
+ }
var _captureStats = (aResponse, aRequestBody) => {
if (!isMap(aResponse)) {
_lastStats = __
@@ -207,13 +275,23 @@ OpenWrap.ai.prototype.__gpttypes = {
if (isDef(aResponse.usage.prompt_tokens)) tokens.prompt = aResponse.usage.prompt_tokens
if (isDef(aResponse.usage.completion_tokens)) tokens.completion = aResponse.usage.completion_tokens
if (isDef(aResponse.usage.total_tokens)) tokens.total = aResponse.usage.total_tokens
+ if (isDef(aResponse.usage.input_tokens)) tokens.prompt = aResponse.usage.input_tokens
+ if (isDef(aResponse.usage.output_tokens)) tokens.completion = aResponse.usage.output_tokens
+ if (isDef(aResponse.usage.total_tokens)) tokens.total = aResponse.usage.total_tokens
if (isMap(aResponse.usage.prompt_tokens_details)) {
if (isDef(aResponse.usage.prompt_tokens_details.cached_tokens)) tokens.cached = aResponse.usage.prompt_tokens_details.cached_tokens
if (isDef(aResponse.usage.prompt_tokens_details.audio_tokens)) tokens.audio = aResponse.usage.prompt_tokens_details.audio_tokens
}
+ if (isMap(aResponse.usage.input_tokens_details)) {
+ if (isDef(aResponse.usage.input_tokens_details.cached_tokens)) tokens.cached = aResponse.usage.input_tokens_details.cached_tokens
+ if (isDef(aResponse.usage.input_tokens_details.audio_tokens)) tokens.audio = aResponse.usage.input_tokens_details.audio_tokens
+ }
if (isMap(aResponse.usage.completion_tokens_details)) {
if (isDef(aResponse.usage.completion_tokens_details.reasoning_tokens)) tokens.reasoning = aResponse.usage.completion_tokens_details.reasoning_tokens
}
+ if (isMap(aResponse.usage.output_tokens_details)) {
+ if (isDef(aResponse.usage.output_tokens_details.reasoning_tokens)) tokens.reasoning = aResponse.usage.output_tokens_details.reasoning_tokens
+ }
if (Object.keys(tokens).length > 0) stats.tokens = tokens
stats.usage = aResponse.usage
}
@@ -718,29 +796,57 @@ OpenWrap.ai.prototype.__gpttypes = {
_r.conversation = msgs
if (_noSystem) msgs = msgs.map(m => { if (m.role == "system") m.role = "developer"; return m })
- var body = {
- model: aModel,
+ var _schema = _normalizeResponseSchema(aResponseSchema)
+ var _responsesBody = {
+ model : aModel,
temperature: aTemperature,
- messages: msgs
+ input : _toResponsesInput(msgs)
+ }
+ if (isDef(_schema)) {
+ _responsesBody.text = {
+ format: merge({ type: "json_schema" }, _schema)
+ }
}
- if (isDef(aResponseSchema) && isMap(aResponseSchema)) {
- body.response_format = {
- type: "json_schema",
- json_schema: {
- name: aResponseSchema.name || "Response",
- description: aResponseSchema.description || "API Response",
- schema: aResponseSchema.schema || {},
- strict: aResponseSchema.strict !== false
- }
+ var _chatBody = {
+ model : aModel,
+ temperature: aTemperature,
+ messages : msgs
+ }
+ if (isDef(_schema)) {
+ _chatBody.response_format = {
+ type : "json_schema",
+ json_schema: _schema
}
}
- body = merge(body, aOptions.params)
- if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'client'}, merge({_t:nowNano(),_f:'client'}, body))
- var _res = _r._request(_route("chat/completions", aModel), body)
- if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'llm'}, merge({_t:nowNano(),_f:'llm'}, _res))
- _captureStats(_res, body)
+ _responsesBody = merge(_responsesBody, aOptions.params)
+ _chatBody = merge(_chatBody, aOptions.params)
+
+ var _res = __
+ var _requestBody = __
+ if (_responsesAvailable !== false) {
+ _requestBody = _responsesBody
+ if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'client'}, merge({_t:nowNano(),_f:'client'}, _requestBody))
+ _res = _r._request(_route("responses", aModel), _requestBody)
+ if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'llm'}, merge({_t:nowNano(),_f:'llm'}, _res))
+ if (isMap(_res) && isMap(_res.error) && _isResponsesUnavailable(_res)) {
+ _responsesAvailable = false
+ _requestBody = _chatBody
+ if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'client'}, merge({_t:nowNano(),_f:'client'}, _requestBody))
+ _res = _r._request(_route("chat/completions", aModel), _requestBody)
+ if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'llm'}, merge({_t:nowNano(),_f:'llm'}, _res))
+ } else if (!isMap(_res) || isUnDef(_res.error)) {
+ _responsesAvailable = true
+ }
+ } else {
+ _requestBody = _chatBody
+ if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'client'}, merge({_t:nowNano(),_f:'client'}, _requestBody))
+ _res = _r._request(_route("chat/completions", aModel), _requestBody)
+ if (isDef(_debugCh)) $ch(_debugCh).set({_t:nowNano(),_f:'llm'}, merge({_t:nowNano(),_f:'llm'}, _res))
+ }
+
+ _captureStats(_res, _requestBody)
return _res
},
rawImgGen: (aPrompt, aModel) => {
@@ -3630,6 +3736,25 @@ OpenWrap.ai.prototype.gpt.prototype.rawPromptWithStats = function(aPrompt, aRole
OpenWrap.ai.prototype.gpt.prototype.jsonSchemaPrompt = function(aPrompt, aResponseSchema, aModel, aTemperature, tools) {
if (!isFunction(this.model.rawResponse)) throw "JSON Schema responses not supported by this provider"
var response = this.model.rawResponse(aPrompt, aModel, aTemperature, false, tools, aResponseSchema)
+ if (isString(response.output_text) && response.output_text.length > 0) {
+ return jsonParse(response.output_text, __, __, true)
+ }
+ if (isArray(response.output)) {
+ var _parts = []
+ response.output.forEach(o => {
+ if (isArray(o.content)) {
+ o.content.forEach(c => {
+ if (isString(c.text)) _parts.push(c.text)
+ else if (isDef(c.json)) _parts.push(c.json)
+ })
+ }
+ })
+ if (_parts.length > 0) {
+ var _out = _parts[0]
+ if (_parts.length > 1 && _parts.every(isString)) _out = _parts.join("")
+ return isString(_out) ? jsonParse(_out, __, __, true) : _out
+ }
+ }
if (isArray(response.choices) && response.choices.length > 0) {
if (response.choices[0].finish_reason == "stop") {
var content = response.choices[0].message.content
diff --git a/tests/autoTestAll.AI.js b/tests/autoTestAll.AI.js
index d86fc725..5b60013e 100644
--- a/tests/autoTestAll.AI.js
+++ b/tests/autoTestAll.AI.js
@@ -404,11 +404,13 @@
var openai = new ow.ai.gpt("openai", { key: "test-key", url: "https://api.openai.com" });
var openaiTransport = openai.model.__debugTransport("chat/completions", "gpt-test");
ow.test.assert(openaiTransport.url, "https://api.openai.com/v1/chat/completions", "Problem building default OpenAI chat URL.");
+ ow.test.assert(openai.model.__debugTransport("responses", "gpt-test").url, "https://api.openai.com/v1/responses", "Problem building default OpenAI responses URL.");
ow.test.assert(isDef(openaiTransport.headers.Authorization), true, "Problem setting default OpenAI bearer authorization.");
ow.test.assert(isUnDef(openaiTransport.headers["api-key"]), true, "Problem avoiding api-key on default OpenAI transport.");
var openaiBaseUrl = new ow.ai.gpt("openai", { key: "test-key", url: "https://api.openai.com/v1" });
ow.test.assert(openaiBaseUrl.model.__debugTransport("chat/completions", "gpt-test").url, "https://api.openai.com/v1/chat/completions", "Problem preserving an OpenAI v1 base URL.");
+ ow.test.assert(openaiBaseUrl.model.__debugTransport("responses", "gpt-test").url, "https://api.openai.com/v1/responses", "Problem preserving an OpenAI v1 responses URL.");
var azureV1 = new ow.ai.gpt("openai", {
key : "test-key",
@@ -417,6 +419,7 @@
});
var azureV1Transport = azureV1.model.__debugTransport("chat/completions", "deployment-a");
ow.test.assert(azureV1Transport.url, "https://example.openai.azure.com/openai/v1/chat/completions", "Problem building Azure OpenAI v1 chat URL.");
+ ow.test.assert(azureV1.model.__debugTransport("responses", "deployment-a").url, "https://example.openai.azure.com/openai/v1/responses", "Problem building Azure OpenAI v1 responses URL.");
ow.test.assert("" + azureV1Transport.headers["api-key"], "test-key", "Problem setting Azure OpenAI v1 api-key header.");
ow.test.assert(isUnDef(azureV1Transport.headers.Authorization), true, "Problem suppressing Azure OpenAI v1 bearer authorization.");
@@ -426,6 +429,7 @@
mode: "azure-openai-v1"
});
ow.test.assert(azureV1BaseUrl.model.__debugTransport("chat/completions", "deployment-a").url, "https://example.openai.azure.com/openai/v1/chat/completions", "Problem preserving an Azure OpenAI v1 base URL.");
+ ow.test.assert(azureV1BaseUrl.model.__debugTransport("responses", "deployment-a").url, "https://example.openai.azure.com/openai/v1/responses", "Problem preserving an Azure OpenAI v1 responses URL.");
var azureLegacy = new ow.ai.gpt("openai", {
key : "test-key",
@@ -436,6 +440,7 @@
});
var azureLegacyTransport = azureLegacy.model.__debugTransport("chat/completions", "ignored");
ow.test.assert(azureLegacyTransport.url, "https://example.openai.azure.com/openai/deployments/deployment-a/chat/completions?api-version=2024-10-21", "Problem building Azure OpenAI legacy deployment chat URL.");
+ ow.test.assert(azureLegacy.model.__debugTransport("responses", "ignored").url, "https://example.openai.azure.com/openai/deployments/deployment-a/responses?api-version=2024-10-21", "Problem building Azure OpenAI legacy deployment responses URL.");
ow.test.assert("" + azureLegacyTransport.headers["api-key"], "test-key", "Problem setting Azure OpenAI legacy api-key header.");
var foundryV1 = new ow.ai.gpt("openai", {
@@ -445,6 +450,7 @@
});
var foundryV1Transport = foundryV1.model.__debugTransport("chat/completions", "deployment-a");
ow.test.assert(foundryV1Transport.url, "https://example.services.ai.azure.com/openai/v1/chat/completions", "Problem building Foundry v1 chat URL.");
+ ow.test.assert(foundryV1.model.__debugTransport("responses", "deployment-a").url, "https://example.services.ai.azure.com/openai/v1/responses", "Problem building Foundry v1 responses URL.");
ow.test.assert("" + foundryV1Transport.headers["api-key"], "test-key", "Problem setting Foundry v1 api-key header.");
var foundryPreview = new ow.ai.gpt("openai", {
@@ -455,6 +461,7 @@
});
var foundryPreviewTransport = foundryPreview.model.__debugTransport("chat/completions", "deployment-a");
ow.test.assert(foundryPreviewTransport.url, "https://example.services.ai.azure.com/models/chat/completions?api-version=2024-05-01-preview", "Problem building Foundry model inference preview chat URL.");
+ ow.test.assert(foundryPreview.model.__debugTransport("responses", "deployment-a").url, "https://example.services.ai.azure.com/models/responses?api-version=2024-05-01-preview", "Problem building Foundry model inference preview responses URL.");
ow.test.assert("" + foundryPreviewTransport.headers["api-key"], "test-key", "Problem setting Foundry preview api-key header.");
};
@@ -840,23 +847,26 @@
requests.push({ url: url, body: __cloneForTest(body) });
return {
model: "gpt-4o",
- choices: [
+ output_text: '{"name":"Alice","age":30}',
+ output: [
{
- finish_reason: "stop",
- message: { role: "assistant", content: '{"name":"Alice","age":30}' }
+ content: [
+ { type: "output_text", text: '{"name":"Alice","age":30}' }
+ ]
}
],
- usage: { prompt_tokens: 20, completion_tokens: 10, total_tokens: 30 }
+ usage: { input_tokens: 20, output_tokens: 10, total_tokens: 30 }
};
};
var rawRes = g.model.rawResponse("Extract: Alice, 30", "gpt-4o", 0.5, false, [], schema);
ow.test.assert(requests.length, 1, "Problem: rawResponse should make exactly one request.");
- ow.test.assert(requests[0].url.indexOf("chat/completions") >= 0, true, "Problem: rawResponse should use chat/completions endpoint.");
- ow.test.assert(requests[0].body.response_format.type, "json_schema", "Problem: rawResponse should set response_format.type to json_schema.");
- ow.test.assert(requests[0].body.response_format.json_schema.name, "PersonInfo", "Problem: rawResponse should pass schema name.");
- ow.test.assert(requests[0].body.response_format.json_schema.strict, true, "Problem: rawResponse should pass strict flag.");
- ow.test.assert(isArray(rawRes.choices) && rawRes.choices.length > 0, true, "Problem: rawResponse should return choices array.");
+ ow.test.assert(requests[0].url.indexOf("responses") >= 0, true, "Problem: rawResponse should prefer the responses endpoint.");
+ ow.test.assert(requests[0].body.text.format.type, "json_schema", "Problem: rawResponse should set text.format.type to json_schema.");
+ ow.test.assert(requests[0].body.text.format.name, "PersonInfo", "Problem: rawResponse should pass schema name.");
+ ow.test.assert(requests[0].body.text.format.strict, true, "Problem: rawResponse should pass strict flag.");
+ ow.test.assert(requests[0].body.input[0].content[0].text, "Extract: Alice, 30", "Problem: rawResponse should convert prompt messages to responses input.");
+ ow.test.assert(rawRes.output_text, '{"name":"Alice","age":30}', "Problem: rawResponse should return the responses payload.");
};
exports.testAIOpenAIJsonSchemaPrompt = function() {
@@ -881,13 +891,14 @@
g.model._request = function(url, body) {
return {
model: "gpt-4o",
- choices: [
+ output: [
{
- finish_reason: "stop",
- message: { role: "assistant", content: '{"name":"Alice","age":30}' }
+ content: [
+ { type: "output_text", text: '{"name":"Alice","age":30}' }
+ ]
}
],
- usage: { prompt_tokens: 20, completion_tokens: 10, total_tokens: 30 }
+ usage: { input_tokens: 20, output_tokens: 10, total_tokens: 30 }
};
};
@@ -917,13 +928,15 @@
g.model._request = function(url, body) {
return {
model: "gpt-4o",
- choices: [
+ output_text: '{"name":"Bob"}',
+ output: [
{
- finish_reason: "stop",
- message: { role: "assistant", content: '{"name":"Bob"}' }
+ content: [
+ { type: "output_text", text: '{"name":"Bob"}' }
+ ]
}
],
- usage: { prompt_tokens: 15, completion_tokens: 8, total_tokens: 23 }
+ usage: { input_tokens: 15, output_tokens: 8, total_tokens: 23 }
};
};
@@ -945,13 +958,15 @@
requests.push(__cloneForTest(body));
return {
model: "gpt-4o",
- choices: [
+ output_text: '{"val":1}',
+ output: [
{
- finish_reason: "stop",
- message: { role: "assistant", content: '{"val":1}' }
+ content: [
+ { type: "output_text", text: '{"val":1}' }
+ ]
}
],
- usage: { prompt_tokens: 5, completion_tokens: 5, total_tokens: 10 }
+ usage: { input_tokens: 5, output_tokens: 5, total_tokens: 10 }
};
};
@@ -960,20 +975,143 @@
name: "MySchema",
schema: { type: "object", additionalProperties: false }
});
- ow.test.assert(requests[0].response_format.json_schema.description, "API Response", "Problem: rawResponse should default description to 'API Response'.");
- ow.test.assert(requests[0].response_format.json_schema.strict, true, "Problem: rawResponse should default strict to true.");
+ ow.test.assert(requests[0].text.format.description, "API Response", "Problem: rawResponse should default description to 'API Response'.");
+ ow.test.assert(requests[0].text.format.strict, true, "Problem: rawResponse should default strict to true.");
+ };
+
+ exports.testAIOpenAIRawResponseFallsBackToChatCompletions = function() {
+ ow.loadAI();
+
+ var g = new ow.ai.gpt("openai", { key: "test-key", model: "gpt-4o" });
+ var requests = [];
+ var schema = {
+ name: "PersonInfo",
+ schema: {
+ type: "object",
+ properties: {
+ name: { type: "string" }
+ },
+ required: ["name"],
+ additionalProperties: false
+ }
+ };
+
+ g.model._request = function(url, body) {
+ requests.push({ url: url, body: __cloneForTest(body) });
+ if (url.indexOf("responses") >= 0) {
+ return {
+ error: {
+ message: "The /responses endpoint is not available for this deployment.",
+ code: "not_found"
+ },
+ status: 404
+ };
+ }
+ return {
+ model: "gpt-4o",
+ choices: [
+ {
+ finish_reason: "stop",
+ message: { role: "assistant", content: '{"name":"Alice"}' }
+ }
+ ],
+ usage: { prompt_tokens: 8, completion_tokens: 4, total_tokens: 12 }
+ };
+ };
+
+ var rawRes = g.model.rawResponse("Extract: Alice", "gpt-4o", 0.5, false, [], schema);
+ ow.test.assert(requests.length, 2, "Problem: rawResponse should retry with chat/completions when responses is unavailable.");
+ ow.test.assert(requests[0].url.indexOf("responses") >= 0, true, "Problem: rawResponse should try responses first.");
+ ow.test.assert(requests[1].url.indexOf("chat/completions") >= 0, true, "Problem: rawResponse should fall back to chat/completions.");
+ ow.test.assert(requests[1].body.response_format.type, "json_schema", "Problem: chat fallback should keep json_schema response_format.");
+ ow.test.assert(isArray(rawRes.choices) && rawRes.choices.length > 0, true, "Problem: chat fallback should return the chat response payload.");
+ };
+
+ exports.testAIOpenAIRawResponseCachesChatFallback = function() {
+ ow.loadAI();
+
+ var g = new ow.ai.gpt("openai", { key: "test-key", model: "gpt-4o" });
+ var requests = [];
+ var schema = {
+ name: "PersonInfo",
+ schema: {
+ type: "object",
+ properties: {
+ name: { type: "string" }
+ },
+ required: ["name"],
+ additionalProperties: false
+ }
+ };
+
+ g.model._request = function(url, body) {
+ requests.push(url);
+ if (url.indexOf("responses") >= 0) {
+ return {
+ error: {
+ message: "The /responses endpoint is not available for this deployment.",
+ code: "not_found"
+ },
+ status: 404
+ };
+ }
+ return {
+ model: "gpt-4o",
+ choices: [
+ {
+ finish_reason: "stop",
+ message: { role: "assistant", content: '{"name":"Alice"}' }
+ }
+ ],
+ usage: { prompt_tokens: 8, completion_tokens: 4, total_tokens: 12 }
+ };
+ };
+
+ g.model.rawResponse("Extract: Alice", "gpt-4o", 0.5, false, [], schema);
+ g.model.rawResponse("Extract: Alice again", "gpt-4o", 0.5, false, [], schema);
+ ow.test.assert(requests.filter(r => r.indexOf("responses") >= 0).length, 1, "Problem: rawResponse should stop retrying responses after endpoint unavailability.");
+ ow.test.assert(requests.filter(r => r.indexOf("chat/completions") >= 0).length, 2, "Problem: rawResponse should reuse chat/completions fallback after responses becomes unavailable.");
+ };
+
+ exports.testAIOpenAIRawResponseDoesNotFallbackOnOtherErrors = function() {
+ ow.loadAI();
+
+ var g = new ow.ai.gpt("openai", { key: "test-key", model: "gpt-4o" });
+ var requests = [];
+
+ g.model._request = function(url, body) {
+ requests.push(url);
+ return {
+ error: {
+ message: "Invalid schema for text.format",
+ code: "invalid_request_error"
+ },
+ status: 400
+ };
+ };
+
+ var res = g.model.rawResponse("Extract: Alice", "gpt-4o", 0.5, false, [], {
+ name: "PersonInfo",
+ schema: { type: "object" }
+ });
+ ow.test.assert(requests.length, 1, "Problem: rawResponse should not fall back for non-availability errors.");
+ ow.test.assert(requests[0].indexOf("responses") >= 0, true, "Problem: rawResponse should return the original responses error.");
+ ow.test.assert(isMap(res.error), true, "Problem: rawResponse should return the original error response.");
};
exports.testAIOpenAIJsonSchemaPromptUnsupportedProvider = function() {
ow.loadAI();
- var g = new ow.ai.gpt("gemini", { key: "test-key" });
- var threw = false;
- try {
- g.jsonSchemaPrompt("test", { name: "T", schema: {} });
- } catch(e) {
- threw = true;
- }
- ow.test.assert(threw, true, "Problem: jsonSchemaPrompt should throw for providers that don't support rawResponse.");
+ [ "gemini", "ollama", "anthropic" ].forEach(provider => {
+ var g = provider == "ollama" ? new ow.ai.gpt(provider, { url: "http://127.0.0.1:11434", model: "llama-test" }) : new ow.ai.gpt(provider, { key: "test-key" });
+ var threw = false;
+ try {
+ g.jsonSchemaPrompt("test", { name: "T", schema: {} });
+ } catch(e) {
+ threw = true;
+ ow.test.assert(String(e), "JSON Schema responses not supported by this provider", "Problem: unsupported providers should throw a consistent error.");
+ }
+ ow.test.assert(threw, true, "Problem: jsonSchemaPrompt should throw for providers that don't support rawResponse.");
+ });
};
})();
diff --git a/tests/autoTestAll.AI.yaml b/tests/autoTestAll.AI.yaml
index 1421e0e1..bf5199a4 100644
--- a/tests/autoTestAll.AI.yaml
+++ b/tests/autoTestAll.AI.yaml
@@ -125,10 +125,10 @@ jobs:
to : oJob Test
exec: args.func = args.tests.testAIOllamaStreamingWarmsModelOnFreshConversation;
- - name: AI::OpenAI rawResponse method
- from: AI::Init
- to : oJob Test
- exec: args.func = args.tests.testAIOpenAIRawResponse;
+ - name: AI::OpenAI rawResponse prefers responses
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIRawResponse;
- name: AI::OpenAI jsonSchemaPrompt
from: AI::Init
@@ -140,15 +140,30 @@ jobs:
to : oJob Test
exec: args.func = args.tests.testAIOpenAIJsonSchemaPromptWithStats;
- - name: AI::OpenAI jsonSchemaPrompt schema defaults
- from: AI::Init
- to : oJob Test
- exec: args.func = args.tests.testAIOpenAIJsonSchemaPromptSchemaDefault;
-
- - name: AI::OpenAI jsonSchemaPrompt unsupported provider
- from: AI::Init
- to : oJob Test
- exec: args.func = args.tests.testAIOpenAIJsonSchemaPromptUnsupportedProvider;
+ - name: AI::OpenAI jsonSchemaPrompt schema defaults
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIJsonSchemaPromptSchemaDefault;
+
+ - name: AI::OpenAI rawResponse fallback to chat completions
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIRawResponseFallsBackToChatCompletions;
+
+ - name: AI::OpenAI rawResponse caches chat fallback
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIRawResponseCachesChatFallback;
+
+ - name: AI::OpenAI rawResponse keeps non availability errors
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIRawResponseDoesNotFallbackOnOtherErrors;
+
+ - name: AI::OpenAI jsonSchemaPrompt unsupported provider
+ from: AI::Init
+ to : oJob Test
+ exec: args.func = args.tests.testAIOpenAIJsonSchemaPromptUnsupportedProvider;
todo:
# ow.ai tests
@@ -176,8 +191,11 @@ todo:
- AI::OpenAI export conversation falls back to tool name
- AI::Ollama streaming tool execution and ids
- AI::Ollama streaming warmup on fresh conversation
- - AI::OpenAI rawResponse method
- - AI::OpenAI jsonSchemaPrompt
- - AI::OpenAI jsonSchemaPromptWithStats
- - AI::OpenAI jsonSchemaPrompt schema defaults
+ - AI::OpenAI rawResponse prefers responses
+ - AI::OpenAI jsonSchemaPrompt
+ - AI::OpenAI jsonSchemaPromptWithStats
+ - AI::OpenAI jsonSchemaPrompt schema defaults
+ - AI::OpenAI rawResponse fallback to chat completions
+ - AI::OpenAI rawResponse caches chat fallback
+ - AI::OpenAI rawResponse keeps non availability errors
- AI::OpenAI jsonSchemaPrompt unsupported provider
From 0046b9eabfe5614e4d635be949071fe3493ee449 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 22:52:21 +0000
Subject: [PATCH 5/8] chore(deps): bump org.snmp4j:snmp4j-agent from 3.8.3 to
3.9.1
Bumps org.snmp4j:snmp4j-agent from 3.8.3 to 3.9.1.
---
updated-dependencies:
- dependency-name: org.snmp4j:snmp4j-agent
dependency-version: 3.9.1
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 8ead1fe2..6077f845 100644
--- a/pom.xml
+++ b/pom.xml
@@ -310,7 +310,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/ma
org.snmp4j
snmp4j-agent
- 3.8.3
+ 3.9.1
io.jsonwebtoken
From 926e4a4158b99d58ddd37ed37c9751a8e70254c9 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 23:01:49 +0000
Subject: [PATCH 6/8] Test results badge
---
tests/results.svg | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/results.svg b/tests/results.svg
index 5f998751..bd046c55 100644
--- a/tests/results.svg
+++ b/tests/results.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
From 4a26c770740d8a4f9d91ff38ebdcb1ba70814235 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 23:01:50 +0000
Subject: [PATCH 7/8] Test results badge
---
tests/asserts.svg | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/asserts.svg b/tests/asserts.svg
index 3c29f2c2..5a880bd4 100644
--- a/tests/asserts.svg
+++ b/tests/asserts.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
From 1d7c49f880a0e021908316b4ef6d9e2af1adf912 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 23:01:52 +0000
Subject: [PATCH 8/8] Test results badge
---
tests/autoTestAll.md | 326 ++++++++++++++++++++++++-------------------
1 file changed, 185 insertions(+), 141 deletions(-)
diff --git a/tests/autoTestAll.md b/tests/autoTestAll.md
index ffa71c30..2b4017f3 100644
--- a/tests/autoTestAll.md
+++ b/tests/autoTestAll.md
@@ -2,205 +2,249 @@
## Summary
-* Number of tests performed: 194
-* Number of tests passed: 194
-* Number of tests failed: 0
-* Number of asserts: 615
+* Number of tests performed: 238
+* Number of tests passed: 228
+* Number of tests failed: 10
+* Number of asserts: 820
## Result details
| Suite | Test | Status | Time | Result |
|-------|------|--------|------|--------------|
-|AI | LSTM AND | PASS | 153 ms | n/a |
-|AI | LSTM network save and load | PASS | 179 ms | n/a |
-|AI | Liquid XOR | PASS | 360 ms | n/a |
-|AI | Liquid network save and load | PASS | 342 ms | n/a |
+|AI | Anthropic prompt caching body and stats | PASS | 9 ms | n/a |
+|AI | Anthropic prompt caching headers | FAIL | 301 ms | TypeError: Cannot read property "requestHeaders" from undefined |
+|AI | GPT prompt argument routing | FAIL | 9 ms | Problem routing GPT rawPromptStream wrapper arguments. (got [ "hello", "gpt-test", 0.4, false, [ { "type": "function", "function": { "name": "echo" } } ], null ] but expected ["hello","gpt-test",0.4,false,[{"type":"function","function":{"name":"echo"}}],null]) |
+|AI | LSTM AND | PASS | 197 ms | n/a |
+|AI | LSTM network save and load | PASS | 143 ms | n/a |
+|AI | Liquid XOR | PASS | 343 ms | n/a |
+|AI | Liquid network save and load | PASS | 369 ms | n/a |
|AI | Normalize features array | PASS | 1 ms | n/a |
-|AI | Normalize with schema | PASS | 10 ms | n/a |
-|AI | Perceptron XOR | PASS | 709 ms | n/a |
-|AI | Perceptron XOR Put | PASS | 6 seconds, 829 ms | n/a |
-|AI | Perceptron XOR save and load | PASS | 47 ms | n/a |
-|AI | Perceptron network save and load | PASS | 36 ms | n/a |
-|AI | Test C45 | PASS | 29 ms | n/a |
-|AI | Test ID3 | PASS | 59 ms | n/a |
-|AI | Test KMeans | PASS | 14 ms | n/a |
-|CSV | Generic CSV | PASS | 3 ms | n/a |
-|CSV | Stream CSV | PASS | 23 ms | n/a |
-|CSV | ToFrom CSV | PASS | 12 ms | n/a |
-|Channels | Keep history util | PASS | 4 seconds, 6 ms | n/a |
-|Channels | Remote channel access auditing | PASS | 256 ms | n/a |
-|Channels | Subscribers test | PASS | 731 ms | n/a |
-|Channels | Test Elastic Index | PASS | 1 ms | n/a |
-|Channels | Test MVS Utils | PASS | 74 ms | n/a |
-|Channels | Test channel (all) | PASS | 14 ms | n/a |
+|AI | Normalize with schema | PASS | 6 ms | n/a |
+|AI | Ollama streaming tool execution and ids | FAIL | 32 ms | Problem preserving Ollama streaming tool result id. (got undefined but expected "call-3") |
+|AI | Ollama streaming warmup on fresh conversation | FAIL | 5 ms | Problem preserving Ollama streaming deltas after warmup. (got [] but expected ["part-1","part-2"]) |
+|AI | OpenAI export conversation falls back to tool name | PASS | 5 ms | n/a |
+|AI | OpenAI jsonSchemaPrompt | PASS | 5 ms | n/a |
+|AI | OpenAI jsonSchemaPrompt schema defaults | PASS | 4 ms | n/a |
+|AI | OpenAI jsonSchemaPrompt unsupported provider | PASS | 6 ms | n/a |
+|AI | OpenAI jsonSchemaPromptWithStats | PASS | 4 ms | n/a |
+|AI | OpenAI rawResponse caches chat fallback | PASS | 6 ms | n/a |
+|AI | OpenAI rawResponse fallback to chat completions | PASS | 3 ms | n/a |
+|AI | OpenAI rawResponse keeps non availability errors | PASS | 3 ms | n/a |
+|AI | OpenAI rawResponse prefers responses | PASS | 8 ms | n/a |
+|AI | OpenAI stats capture includes cached and reasoning | PASS | 12 ms | n/a |
+|AI | OpenAI streaming tool recursion without duplication | PASS | 26 ms | n/a |
+|AI | OpenAI tool recursion without duplication | PASS | 12 ms | n/a |
+|AI | OpenAI transport modes | PASS | 26 ms | n/a |
+|AI | Perceptron XOR | PASS | 481 ms | n/a |
+|AI | Perceptron XOR Put | PASS | 7 seconds, 254 ms | n/a |
+|AI | Perceptron XOR save and load | PASS | 27 ms | n/a |
+|AI | Perceptron network save and load | PASS | 38 ms | n/a |
+|AI | Test C45 | PASS | 30 ms | n/a |
+|AI | Test ID3 | PASS | 48 ms | n/a |
+|AI | Test KMeans | PASS | 12 ms | n/a |
+|CSV | Generic CSV | PASS | 1 ms | n/a |
+|CSV | Stream CSV | PASS | 14 ms | n/a |
+|CSV | ToFrom CSV | PASS | 7 ms | n/a |
+|Channels | Keep history util | PASS | 4 seconds, 8 ms | n/a |
+|Channels | Remote channel access auditing | PASS | 228 ms | n/a |
+|Channels | Subscribers test | PASS | 754 ms | n/a |
+|Channels | Test Elastic Index | PASS | 2 ms | n/a |
+|Channels | Test MVS Utils | PASS | 83 ms | n/a |
+|Channels | Test channel (all) | PASS | 18 ms | n/a |
|Channels | Test channel (big) | PASS | 1 ms | n/a |
-|Channels | Test channel (big) | PASS | 22 ms | n/a |
-|Channels | Test channel (big) | PASS | 235 ms | n/a |
-|Channels | Test channel (big) | PASS | ~0 ms | n/a |
-|Channels | Test channel (big) | PASS | 32 seconds, 7 ms | n/a |
-|Channels | Test channel (cache) | PASS | 2 ms | n/a |
+|Channels | Test channel (big) | PASS | 17 ms | n/a |
+|Channels | Test channel (big) | PASS | 164 ms | n/a |
+|Channels | Test channel (big) | PASS | 1 ms | n/a |
+|Channels | Test channel (big) | PASS | 32 seconds, 5 ms | n/a |
+|Channels | Test channel (cache) | PASS | 1 ms | n/a |
|Channels | Test channel (cache) | PASS | 1 ms | n/a |
-|Channels | Test channel (cache) | PASS | 32 seconds, 12 ms | n/a |
+|Channels | Test channel (cache) | PASS | 32 seconds, 6 ms | n/a |
|Channels | Test channel (file) | PASS | 4 ms | n/a |
-|Channels | Test channel (file) | PASS | 19 ms | n/a |
-|Channels | Test channel (file) | PASS | 185 ms | n/a |
+|Channels | Test channel (file) | PASS | 11 ms | n/a |
+|Channels | Test channel (file) | PASS | 177 ms | n/a |
|Channels | Test channel (file) | PASS | ~0 ms | n/a |
-|Channels | Test channel (file) | PASS | 32 seconds, 4 ms | n/a |
-|Channels | Test channel (mvs) | PASS | 1 ms | n/a |
-|Channels | Test channel (mvs) | PASS | 9 ms | n/a |
-|Channels | Test channel (mvs) | PASS | 109 ms | n/a |
-|Channels | Test channel (mvs) | PASS | 1 ms | n/a |
-|Channels | Test channel (mvs) | PASS | 32 seconds, 4 ms | n/a |
-|Channels | Test channel (simple) | PASS | ~0 ms | n/a |
+|Channels | Test channel (file) | PASS | 32 seconds, 6 ms | n/a |
+|Channels | Test channel (mvs) | PASS | 2 ms | n/a |
+|Channels | Test channel (mvs) | PASS | 8 ms | n/a |
+|Channels | Test channel (mvs) | PASS | 111 ms | n/a |
+|Channels | Test channel (mvs) | PASS | ~0 ms | n/a |
+|Channels | Test channel (mvs) | PASS | 32 seconds, 5 ms | n/a |
+|Channels | Test channel (simple) | PASS | 1 ms | n/a |
|Channels | Test channel (simple) | PASS | 4 ms | n/a |
-|Channels | Test channel (simple) | PASS | 93 ms | n/a |
+|Channels | Test channel (simple) | PASS | 113 ms | n/a |
|Channels | Test channel (simple) | PASS | ~0 ms | n/a |
|Channels | Test channel (simple) | PASS | 32 seconds, 5 ms | n/a |
|DB | DB type conversion to JS | PASS | 7 ms | n/a |
-|DB | Simple DB in memory | PASS | 175 ms | n/a |
-|Format | Add number separator | PASS | 1 ms | n/a |
+|DB | Simple DB in memory | PASS | 199 ms | n/a |
+|Format | Add number separator | PASS | ~0 ms | n/a |
|Format | Conversions | PASS | 6 ms | n/a |
-|Format | Cron | PASS | 3 ms | n/a |
-|Format | Cron How Many Ago | PASS | 29 ms | n/a |
-|Format | Date diff | PASS | 5 ms | n/a |
-|Format | Date to/from | PASS | 2 ms | n/a |
+|Format | Cron | PASS | 2 ms | n/a |
+|Format | Cron How Many Ago | PASS | 26 ms | n/a |
+|Format | Date diff | PASS | 4 ms | n/a |
+|Format | Date to/from | PASS | 5 ms | n/a |
|Format | Escape strings | PASS | 1 ms | n/a |
|Format | Escape/Unescape HTML4 | PASS | 11 ms | n/a |
-|Format | Host | PASS | 3 ms | n/a |
+|Format | Grid | PASS | 59 ms | n/a |
+|Format | Host | PASS | 2 ms | n/a |
|Format | LDAP date to/from | PASS | ~0 ms | n/a |
|Format | LSH | PASS | 591 ms | n/a |
|Format | Load Format | PASS | ~0 ms | n/a |
+|Format | Markdown wrap | PASS | 52 ms | n/a |
|Format | Number abbreviation | PASS | 1 ms | n/a |
-|Format | Number rounding | PASS | ~0 ms | n/a |
-|Format | SLON | PASS | 26 ms | n/a |
+|Format | Number rounding | PASS | 1 ms | n/a |
+|Format | Print bullet value formats | PASS | 17 ms | n/a |
+|Format | Print dashboard | FAIL | 13 ms | Problem with printDashboard total width. (got 38 but expected 40) |
+|Format | Print histogram | PASS | 15 ms | n/a |
+|Format | Print sparkline | PASS | 7 ms | n/a |
+|Format | PrintTable emoji alignment | PASS | 17 ms | n/a |
+|Format | PrintTable header alignment | FAIL | 11 ms | Problem with printTable header second separator alignment. (got 13 but expected 15) |
+|Format | PrintTable subdivision flag alignment | FAIL | 9 ms | Problem with printTable subdivision flag alignment. (got 23 but expected 13) |
+|Format | SLON | PASS | 35 ms | n/a |
|Format | String pad | PASS | ~0 ms | n/a |
+|Format | Terminal capabilities | PASS | 3 ms | n/a |
|Format | Time ago | PASS | 2 ms | n/a |
|Format | TimeAbbreviation | PASS | 4 ms | n/a |
|Format | Unix date to/from | PASS | 1 ms | n/a |
-|Format | Word wrap | PASS | 6 ms | n/a |
-|HTTP | HTTP changing user agent | PASS | 331 ms | n/a |
-|HTTP | HTTP plugin basic functionality | PASS | 53 ms | n/a |
-|HTTP | HTTP plugin test basic auth | PASS | 623 ms | n/a |
-|HTTP | HTTP plugin web socket client | PASS | 3 seconds, 680 ms | n/a |
-|IO | IO Test Gzip Native to Byte array | PASS | 28 ms | n/a |
-|IO | IO Test JSON | PASS | 2 ms | n/a |
-|IO | IO Test Stream JSON | PASS | 34 ms | n/a |
-|IO | IO Test TAR functionality | PASS | 4 seconds, 736 ms | n/a |
-|IO | IO Test binary file detection | PASS | 13 ms | n/a |
-|IO | IO Test copy streams | PASS | 220 ms | n/a |
+|Format | Viz benchmark | PASS | 2 ms | n/a |
+|Format | Viz create canvas | PASS | 3 ms | n/a |
+|Format | Viz frame diff | PASS | 2 ms | n/a |
+|Format | Viz layout | PASS | 14 ms | n/a |
+|Format | Word wrap | PASS | 14 ms | n/a |
+|HTTP | HTTP changing user agent | PASS | 388 ms | n/a |
+|HTTP | HTTP plugin basic functionality | PASS | 55 ms | n/a |
+|HTTP | HTTP plugin test basic auth | PASS | 839 ms | n/a |
+|HTTP | HTTP plugin web socket client | PASS | 2 seconds, 652 ms | n/a |
+|IO | IO Test Gzip Native to Byte array | PASS | 26 ms | n/a |
+|IO | IO Test JSON | PASS | 3 ms | n/a |
+|IO | IO Test Stream JSON | PASS | 30 ms | n/a |
+|IO | IO Test TAR functionality | PASS | 4 seconds, 423 ms | n/a |
+|IO | IO Test binary file detection | PASS | 11 ms | n/a |
+|IO | IO Test copy streams | PASS | 174 ms | n/a |
|IO | IO Test copy/move/delete file | PASS | 6 ms | n/a |
-|IO | IO Test read/writeFileStream | PASS | 6 ms | n/a |
-|IO | IO Test read/writeFileStream NIO | PASS | 6 ms | n/a |
+|IO | IO Test read/writeFileStream | PASS | 3 ms | n/a |
+|IO | IO Test read/writeFileStream NIO | PASS | 4 ms | n/a |
|IO | IO Test read/writeFileStreamBytes | PASS | 2 ms | n/a |
-|IO | IO Test read/writeFileStreamBytes NIO | PASS | 1 ms | n/a |
-|JMX | JMX test | PASS | 562 ms | n/a |
-|Java | Java ASym Cipher | PASS | 172 ms | n/a |
-|Java | Java Cipher | PASS | 278 ms | n/a |
-|Net | Get Actual Time (default) | PASS | 37 ms | n/a |
-|Net | Get Actual Time (server and timeout) | PASS | 43 ms | n/a |
-|Net | Get Actual Time (with server) | PASS | 20 ms | n/a |
-|Net | Get Actual Time (with timeout) | PASS | 20 ms | n/a |
+|IO | IO Test read/writeFileStreamBytes NIO | PASS | 2 ms | n/a |
+|JMX | JMX test | PASS | 920 ms | n/a |
+|Java | Java ASym Cipher | PASS | 133 ms | n/a |
+|Java | Java Cipher | PASS | 269 ms | n/a |
+|MCP | OAuth authorization URL includes resource and PKCE | PASS | 36 ms | n/a |
+|MCP | OAuth authorization code token exchange includes resource and verifier | PASS | 73 ms | n/a |
+|MCP | OAuth discovery with client credentials | PASS | 146 ms | n/a |
+|MCP | oJob tplDesc renders all tool metadata strings | PASS | 311 ms | n/a |
+|Net | Get Actual Time (default) | PASS | 226 ms | n/a |
+|Net | Get Actual Time (server and timeout) | PASS | 19 ms | n/a |
+|Net | Get Actual Time (with server) | PASS | 173 ms | n/a |
+|Net | Get Actual Time (with timeout) | PASS | 173 ms | n/a |
|Net | Load Net | PASS | ~0 ms | n/a |
|Obj | Array to object conversion | PASS | 1 ms | n/a |
-|Obj | DB ResultSet to object | PASS | 1 second, 421 ms | n/a |
-|Obj | Filter | PASS | 9 ms | n/a |
+|Obj | DB ResultSet to object | PASS | 1 second, 446 ms | n/a |
+|Obj | Filter | PASS | 21 ms | n/a |
|Obj | Flat map | PASS | 13 ms | n/a |
-|Obj | Flatten | PASS | 211 ms | n/a |
-|Obj | Generate One of | PASS | 42 ms | n/a |
-|Obj | Generate One of Fn | PASS | 15 ms | n/a |
+|Obj | Flatten | PASS | 205 ms | n/a |
+|Obj | Generate One of | PASS | 25 ms | n/a |
+|Obj | Generate One of Fn | PASS | 12 ms | n/a |
|Obj | Get Path | PASS | 1 ms | n/a |
-|Obj | Object fuzzy search | PASS | 45 ms | n/a |
-|Obj | Object pool | PASS | 6 seconds, 79 ms | n/a |
-|Obj | Object schema validation | PASS | 310 ms | n/a |
-|Obj | Object to array conversion | PASS | 2 ms | n/a |
+|Obj | Object fuzzy search | PASS | 47 ms | n/a |
+|Obj | Object pool | PASS | 6 seconds, 77 ms | n/a |
+|Obj | Object schema validation | PASS | 263 ms | n/a |
+|Obj | Object to array conversion | PASS | 1 ms | n/a |
|Obj | Set Path | PASS | 1 ms | n/a |
-|Obj | Sign object | PASS | 422 ms | n/a |
-|Obj | Thread-safe array | PASS | 2 ms | n/a |
+|Obj | Sign object | PASS | 413 ms | n/a |
+|Obj | Thread-safe array | PASS | 4 ms | n/a |
|Obj | Thread-safe array | PASS | ~0 ms | n/a |
|Obj | Thread-safe map | PASS | 2 ms | n/a |
-|OpenAF | Await | PASS | 13 ms | n/a |
-|OpenAF | AwaitAll | PASS | 54 ms | n/a |
-|OpenAF | Basic Parallel processing | PASS | 12 ms | n/a |
-|OpenAF | Cache | PASS | 330 ms | n/a |
-|OpenAF | Crypt | PASS | 10 ms | n/a |
+|OpenAF | Await | PASS | 57 ms | n/a |
+|OpenAF | AwaitAll | PASS | 55 ms | n/a |
+|OpenAF | Basic Parallel processing | PASS | 10 ms | n/a |
+|OpenAF | Cache | PASS | 321 ms | n/a |
+|OpenAF | Crypt | PASS | 34 ms | n/a |
|OpenAF | DescType | PASS | 2 ms | n/a |
|OpenAF | Encrypt/Decrypt | PASS | ~0 ms | n/a |
|OpenAF | Get Path | PASS | 1 ms | n/a |
-|OpenAF | Get version | PASS | 2 ms | n/a |
+|OpenAF | Get version | PASS | 1 ms | n/a |
|OpenAF | IsFunctions | PASS | 1 ms | n/a |
-|OpenAF | Java RegExp | PASS | 9 ms | n/a |
-|OpenAF | Logs | PASS | 4 ms | n/a |
+|OpenAF | Java RegExp | PASS | 4 ms | n/a |
+|OpenAF | Logs | PASS | 2 ms | n/a |
|OpenAF | Map Array | PASS | 1 ms | n/a |
-|OpenAF | Map22Array | PASS | 516 ms | n/a |
-|OpenAF | NDJSON | PASS | 4 ms | n/a |
-|OpenAF | PSelect | PASS | 7 ms | n/a |
+|OpenAF | Map22Array | PASS | 567 ms | n/a |
+|OpenAF | NDJSON | PASS | 2 ms | n/a |
+|OpenAF | PSelect | PASS | 9 ms | n/a |
|OpenAF | Prints | PASS | 1 ms | n/a |
-|OpenAF | Queue | PASS | 16 ms | n/a |
+|OpenAF | Queue | PASS | 14 ms | n/a |
|OpenAF | Range | PASS | 4 ms | n/a |
|OpenAF | Retry | PASS | 1 ms | n/a |
|OpenAF | SPrints | PASS | 1 ms | n/a |
|OpenAF | Search Key and Values | PASS | 6 ms | n/a |
-|OpenAF | Set Path | PASS | 2 ms | n/a |
-|OpenAF | Test $do | PASS | 15 ms | n/a |
-|OpenAF | Test $doAll | PASS | 8 ms | n/a |
-|OpenAF | Test $doFirst | PASS | 157 ms | n/a |
+|OpenAF | Set Path | PASS | 1 ms | n/a |
+|OpenAF | Test $do | PASS | 11 ms | n/a |
+|OpenAF | Test $doAll | PASS | 5 ms | n/a |
+|OpenAF | Test $doFirst | PASS | 153 ms | n/a |
|OpenAF | Test AF Parse | PASS | 1 ms | n/a |
-|OpenAF | Test BCrypt | PASS | 1 second, 542 ms | n/a |
+|OpenAF | Test BCrypt | PASS | 772 ms | n/a |
|OpenAF | Test Encoding | PASS | 1 ms | n/a |
|OpenAF | Test Envs | PASS | 1 ms | n/a |
-|OpenAF | Test FLock | PASS | 5 ms | n/a |
+|OpenAF | Test FLock | PASS | 6 ms | n/a |
|OpenAF | Test Format Conversion to/from base64 | PASS | ~0 ms | n/a |
|OpenAF | Test Format Conversion to/from bytes | PASS | ~0 ms | n/a |
|OpenAF | Test GetSet | PASS | 3 ms | n/a |
-|OpenAF | Test Lock | PASS | 1 ms | n/a |
+|OpenAF | Test Lock | PASS | 2 ms | n/a |
|OpenAF | Test Merge | PASS | ~0 ms | n/a |
|OpenAF | Test Object Compression | PASS | ~0 ms | n/a |
-|OpenAF | Test Path | PASS | 115 ms | n/a |
-|OpenAF | Test Rest | PASS | 296 ms | n/a |
+|OpenAF | Test Path | PASS | 202 ms | n/a |
+|OpenAF | Test Rest | PASS | 358 ms | n/a |
|OpenAF | Test SHA1 | PASS | ~0 ms | n/a |
|OpenAF | Test SHA256 | PASS | ~0 ms | n/a |
|OpenAF | Test SHA512 | PASS | ~0 ms | n/a |
|OpenAF | Test Scope Ids | PASS | 1 ms | n/a |
-|OpenAF | Test Shell | PASS | 3 ms | n/a |
+|OpenAF | Test Shell | PASS | 2 ms | n/a |
|OpenAF | Test Shell with Map | PASS | 2 ms | n/a |
-|OpenAF | Test Stream conversions | PASS | 1 ms | n/a |
+|OpenAF | Test Stream conversions | PASS | ~0 ms | n/a |
|OpenAF | Test Void shortcut | PASS | ~0 ms | n/a |
-|OpenAF | Test clone | PASS | 2 ms | n/a |
-|OpenAF | Test pForEach | PASS | 135 ms | n/a |
-|OpenAF | Thread box | PASS | 306 ms | n/a |
+|OpenAF | Test clone | PASS | ~0 ms | n/a |
+|OpenAF | Test pForEach | PASS | 155 ms | n/a |
+|OpenAF | Thread box | PASS | 308 ms | n/a |
|OpenAF | Two factor authentication | PASS | 2 ms | n/a |
-|OpenAF | XML2And4Obj | PASS | 211 ms | n/a |
-|OpenAF | YAML | PASS | 5 ms | n/a |
-|Sec | Sec basic functionality | PASS | 15 ms | n/a |
-|Sec | Sec function functionality | PASS | 458 ms | n/a |
-|Sec | Sec object functionality | PASS | 885 ms | n/a |
-|Server | Auth | PASS | 6 seconds, 18 ms | n/a |
-|Server | AuthApp | PASS | 163 ms | n/a |
-|Server | HTTP server | PASS | 110 ms | n/a |
-|Server | HTTP server Java | PASS | 20 ms | n/a |
+|OpenAF | XML2And4Obj | PASS | 302 ms | n/a |
+|OpenAF | YAML | PASS | 6 ms | n/a |
+|Sec | Sec basic functionality | PASS | 14 ms | n/a |
+|Sec | Sec function functionality | PASS | 327 ms | n/a |
+|Sec | Sec object functionality | PASS | 959 ms | n/a |
+|Server | Auth | PASS | 6 seconds, 16 ms | n/a |
+|Server | AuthApp | PASS | 162 ms | n/a |
+|Server | CheckIn | PASS | 3 ms | n/a |
+|Server | HTTP server | PASS | 140 ms | n/a |
+|Server | HTTP server Java | PASS | 22 ms | n/a |
|Server | HTTP server NWU2 | PASS | 76 ms | n/a |
-|Server | Locks | PASS | 2 seconds, 22 ms | n/a |
-|Server | Queue | PASS | 4 seconds, 669 ms | n/a |
-|Server | REST server | PASS | 102 ms | n/a |
-|Server | REST server Java | PASS | 177 ms | n/a |
-|Server | REST server NWU2 | PASS | 66 ms | n/a |
-|Server | REST server simple | PASS | 32 ms | n/a |
+|Server | HTTP server prefix | FAIL | 20 ms | (nwu) Problem stripping HTTPD prefix before route handlers. (got { "responseCode": 200, "contentType": "text/plain", "response": "/normal" } but expected "/normal") |
+|Server | HTTP server prefix Java | FAIL | 10 ms | (java) Problem stripping HTTPD prefix before route handlers. (got { "responseCode": 200, "contentType": "text/plain", "response": "/normal" } but expected "/normal") |
+|Server | HTTP server prefix NWU2 | FAIL | 19 ms | (nwu2) Problem stripping HTTPD prefix before route handlers. (got { "responseCode": 200, "contentType": "text/plain", "response": "/normal" } but expected "/normal") |
+|Server | HTTP server prefix helpers | PASS | 4 ms | n/a |
+|Server | Locks | PASS | 2 seconds, 15 ms | n/a |
+|Server | Queue | PASS | 5 seconds, 74 ms | n/a |
+|Server | REST server | PASS | 66 ms | n/a |
+|Server | REST server Java | PASS | 288 ms | n/a |
+|Server | REST server NWU2 | PASS | 48 ms | n/a |
+|Server | REST server simple | PASS | 37 ms | n/a |
|Server | REST server simple Java | PASS | 22 ms | n/a |
-|Server | REST server simple NWU2 | PASS | 28 ms | n/a |
-|Server | Scheduler | PASS | 16 seconds, 24 ms | n/a |
+|Server | REST server simple NWU2 | PASS | 34 ms | n/a |
+|Server | Scheduler | PASS | 16 seconds, 26 ms | n/a |
|Template | Load Template | PASS | ~0 ms | n/a |
-|Template | Test Markdown to HTML | PASS | 654 ms | n/a |
-|Template | Test conditional helpers | PASS | 224 ms | n/a |
-|Template | Test format helpers | PASS | 63 ms | n/a |
-|Template | Test openaf helpers | PASS | 127 ms | n/a |
-|Template | Test partial helpers | PASS | 38 ms | n/a |
-|Template | Test simple template | PASS | 53 ms | n/a |
-|ZIP | ZIP basic functionality | PASS | 1 second, 462 ms | n/a |
-|ZIP | ZIP streaming functionality | PASS | 1 second, 496 ms | n/a |
-|oDoc | Test access to oDoc | PASS | 1 second, 4 ms | n/a |
-|oJob | oJob | PASS | 1 second, 672 ms | n/a |
-|oJob | oJobArgsMultipleLevels | PASS | 1 second, 980 ms | n/a |
-|oJob | oJobChecks | PASS | 1 second, 671 ms | n/a |
-|oJob | oJobInitArray | PASS | 4 seconds, 100 ms | n/a |
-|oJob | oJobPass | PASS | 1 second, 813 ms | n/a |
-|oJob | oJobShortcutOutput | PASS | 1 second, 888 ms | n/a |
-|oJob | oJobShortcuts | PASS | 1 second, 780 ms | n/a |
+|Template | Test Markdown to HTML | PASS | 622 ms | n/a |
+|Template | Test Markdown to HTML prefix | PASS | 166 ms | n/a |
+|Template | Test conditional helpers | PASS | 189 ms | n/a |
+|Template | Test format helpers | PASS | 54 ms | n/a |
+|Template | Test openaf helpers | PASS | 94 ms | n/a |
+|Template | Test partial helpers | PASS | 41 ms | n/a |
+|Template | Test simple template | PASS | 43 ms | n/a |
+|ZIP | ZIP basic functionality | PASS | 1 second, 517 ms | n/a |
+|ZIP | ZIP streaming functionality | PASS | 1 second, 532 ms | n/a |
+|oDoc | Test access to oDoc | PASS | 925 ms | n/a |
+|oJob | oJob | PASS | 1 second, 895 ms | n/a |
+|oJob | oJobArgsMultipleLevels | PASS | 1 second, 994 ms | n/a |
+|oJob | oJobChecks | PASS | 1 second, 854 ms | n/a |
+|oJob | oJobEncryptedJSON | PASS | 1 second, 725 ms | n/a |
+|oJob | oJobEncryptedYAML | PASS | 1 second, 834 ms | n/a |
+|oJob | oJobInitArray | PASS | 4 seconds, 209 ms | n/a |
+|oJob | oJobPass | PASS | 1 second, 985 ms | n/a |
+|oJob | oJobShortcutOutput | PASS | 1 second, 910 ms | n/a |
+|oJob | oJobShortcuts | PASS | 1 second, 948 ms | n/a |