@oh-my-pi/pi-ai 16.0.2 → 16.0.3
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.0.3] - 2026-06-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Exported `renderDelimitedThinking` from the `@oh-my-pi/pi-ai/dialect` barrel so consumers can reuse the dialect's `<thinking>` envelope unwrap-and-rewrap logic (the only `./dialect/rendering` primitive re-exported; the rest stay dialect-internal).
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed OpenAI Responses/Codex tool schema normalization stripping provider-rejected regex lookaround patterns from MCP tool parameter schemas. ([#2784](https://github.com/can1357/oh-my-pi/issues/2784))
|
|
14
|
+
- Fixed OpenAI Responses parallel tool-call routing so late keyed argument deltas for a closed call are dropped instead of being appended to another open call.
|
|
15
|
+
|
|
5
16
|
## [16.0.2] - 2026-06-16
|
|
6
17
|
|
|
7
18
|
### Added
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "16.0.
|
|
4
|
+
"version": "16.0.3",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bufbuild/protobuf": "^2.12.0",
|
|
41
|
-
"@oh-my-pi/pi-catalog": "16.0.
|
|
42
|
-
"@oh-my-pi/pi-utils": "16.0.
|
|
41
|
+
"@oh-my-pi/pi-catalog": "16.0.3",
|
|
42
|
+
"@oh-my-pi/pi-utils": "16.0.3",
|
|
43
43
|
"partial-json": "^0.1.7",
|
|
44
44
|
"zod": "^4"
|
|
45
45
|
},
|
package/src/dialect/index.ts
CHANGED
|
@@ -5,4 +5,9 @@ export * from "./factory";
|
|
|
5
5
|
export * from "./history";
|
|
6
6
|
export * from "./inventory";
|
|
7
7
|
export * from "./owned-stream";
|
|
8
|
+
// `./rendering` is a dialect-internal primitives module deliberately excluded
|
|
9
|
+
// from the barrel. `renderDelimitedThinking` is the one helper an external
|
|
10
|
+
// consumer needs (the legacy markdown `/dump` reuses its `<thinking>` envelope
|
|
11
|
+
// unwrap), so re-export only that symbol rather than `export *`-ing the rest.
|
|
12
|
+
export { renderDelimitedThinking } from "./rendering";
|
|
8
13
|
export * from "./types";
|
|
@@ -525,6 +525,7 @@ export async function processResponsesStream<TApi extends Api>(
|
|
|
525
525
|
lastOpenItem = entry;
|
|
526
526
|
};
|
|
527
527
|
const lookupOpenItem = (event: { output_index?: number; item_id?: string }): StreamingItem | undefined => {
|
|
528
|
+
const hasKey = typeof event.output_index === "number" || event.item_id !== undefined;
|
|
528
529
|
if (typeof event.output_index === "number") {
|
|
529
530
|
const found = openItemsByOutputIndex.get(event.output_index);
|
|
530
531
|
if (found) return found;
|
|
@@ -533,8 +534,10 @@ export async function processResponsesStream<TApi extends Api>(
|
|
|
533
534
|
const found = openItemsByItemId.get(event.item_id);
|
|
534
535
|
if (found) return found;
|
|
535
536
|
}
|
|
536
|
-
//
|
|
537
|
-
|
|
537
|
+
// Keyed events whose item already closed are stale; drop them instead of
|
|
538
|
+
// routing to a sibling. Only fully identifierless mock/proxy events use the
|
|
539
|
+
// legacy singleton fallback.
|
|
540
|
+
return hasKey ? undefined : (lastOpenItem ?? undefined);
|
|
538
541
|
};
|
|
539
542
|
const hasOpenItemKey = (event: { output_index?: number; item_id?: string }): boolean =>
|
|
540
543
|
typeof event.output_index === "number" || event.item_id !== undefined;
|
|
@@ -936,8 +936,25 @@ export function sanitizeSchemaForOpenAIResponses(schema: JsonObject): JsonObject
|
|
|
936
936
|
* `normalizeSchemaFor*` dispatcher naming used elsewhere in this module.
|
|
937
937
|
*/
|
|
938
938
|
export const normalizeSchemaForOpenAIResponses: (schema: JsonObject) => JsonObject = sanitizeSchemaForOpenAIResponses;
|
|
939
|
+
const OPENAI_UNSUPPORTED_REGEX_LOOKAROUNDS = new Set(["=", "!", "<=", "<!"]);
|
|
940
|
+
const OPENAI_RESPONSES_PATTERN_PROPERTIES_FALLBACK = ".*";
|
|
939
941
|
|
|
940
|
-
function
|
|
942
|
+
function hasOpenAIUnsupportedRegexLookaround(pattern: string): boolean {
|
|
943
|
+
let groupStart = pattern.indexOf("(?");
|
|
944
|
+
while (groupStart !== -1) {
|
|
945
|
+
let escapes = 0;
|
|
946
|
+
for (let i = groupStart - 1; i >= 0 && pattern[i] === "\\"; i--) escapes++;
|
|
947
|
+
if (escapes % 2 === 0) {
|
|
948
|
+
const operator =
|
|
949
|
+
pattern[groupStart + 2] === "<" ? pattern.slice(groupStart + 2, groupStart + 4) : pattern[groupStart + 2];
|
|
950
|
+
if (OPENAI_UNSUPPORTED_REGEX_LOOKAROUNDS.has(operator)) return true;
|
|
951
|
+
}
|
|
952
|
+
groupStart = pattern.indexOf("(?", groupStart + 2);
|
|
953
|
+
}
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
function normalizeOpenAIResponsesSchemaNode(value: unknown, cache: WeakMap<JsonObject, unknown>): unknown {
|
|
941
958
|
if (!isJsonObject(value)) return value;
|
|
942
959
|
|
|
943
960
|
// `{}` (empty JSON Schema) ≡ `true` (JSON Schema draft 2020-12 §4.3.1).
|
|
@@ -973,11 +990,21 @@ function normalizeOpenAIResponsesSchemaNode(value: unknown, cache: WeakMap<JsonO
|
|
|
973
990
|
changed = true;
|
|
974
991
|
continue;
|
|
975
992
|
}
|
|
993
|
+
if (
|
|
994
|
+
key === "pattern" &&
|
|
995
|
+
typeof value.pattern === "string" &&
|
|
996
|
+
hasOpenAIUnsupportedRegexLookaround(value.pattern)
|
|
997
|
+
) {
|
|
998
|
+
changed = true;
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
976
1001
|
|
|
977
1002
|
const child = value[key];
|
|
978
1003
|
let next: unknown = child;
|
|
979
|
-
if (
|
|
980
|
-
next = normalizeOpenAIResponsesSchemaMap(child, cache);
|
|
1004
|
+
if (key === "patternProperties" && isJsonObject(child)) {
|
|
1005
|
+
next = normalizeOpenAIResponsesSchemaMap(child, cache, true);
|
|
1006
|
+
} else if (OPENAI_RESPONSES_SCHEMA_MAP_KEYS.has(key) && isJsonObject(child)) {
|
|
1007
|
+
next = normalizeOpenAIResponsesSchemaMap(child, cache, false);
|
|
981
1008
|
} else if (OPENAI_RESPONSES_SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(child)) {
|
|
982
1009
|
next = normalizeOpenAIResponsesSchemaArray(child, cache);
|
|
983
1010
|
} else if (OPENAI_RESPONSES_SCHEMA_VALUE_KEYS.has(key) && isJsonObject(child)) {
|
|
@@ -1008,7 +1035,7 @@ function normalizeOpenAIResponsesSchemaNode(value: unknown, cache: WeakMap<JsonO
|
|
|
1008
1035
|
// the seeded partial and set `changed = true` for that node, so a node
|
|
1009
1036
|
// that finishes with `changed === false` is provably non-cyclic and
|
|
1010
1037
|
// referentially equal to its input.
|
|
1011
|
-
const result = changed ? output : value;
|
|
1038
|
+
const result = changed ? (isJsonObjectEmpty(output) ? true : output) : value;
|
|
1012
1039
|
cache.set(value, result);
|
|
1013
1040
|
return result;
|
|
1014
1041
|
}
|
|
@@ -1022,7 +1049,7 @@ function declaresObjectType(type: unknown): boolean {
|
|
|
1022
1049
|
return false;
|
|
1023
1050
|
}
|
|
1024
1051
|
|
|
1025
|
-
function normalizeOpenAIResponsesSchemaArray(value: unknown[], cache: WeakMap<JsonObject,
|
|
1052
|
+
function normalizeOpenAIResponsesSchemaArray(value: unknown[], cache: WeakMap<JsonObject, unknown>): unknown[] {
|
|
1026
1053
|
let changed = false;
|
|
1027
1054
|
const output = value.map(item => {
|
|
1028
1055
|
const next = normalizeOpenAIResponsesSchemaNode(item, cache);
|
|
@@ -1032,7 +1059,11 @@ function normalizeOpenAIResponsesSchemaArray(value: unknown[], cache: WeakMap<Js
|
|
|
1032
1059
|
return changed ? output : value;
|
|
1033
1060
|
}
|
|
1034
1061
|
|
|
1035
|
-
function normalizeOpenAIResponsesSchemaMap(
|
|
1062
|
+
function normalizeOpenAIResponsesSchemaMap(
|
|
1063
|
+
schemaMap: JsonObject,
|
|
1064
|
+
cache: WeakMap<JsonObject, unknown>,
|
|
1065
|
+
stripUnsupportedRegexKeys: boolean,
|
|
1066
|
+
): JsonObject {
|
|
1036
1067
|
let changed = false;
|
|
1037
1068
|
const output: JsonObject = {};
|
|
1038
1069
|
for (const key in schemaMap) {
|
|
@@ -1040,11 +1071,29 @@ function normalizeOpenAIResponsesSchemaMap(schemaMap: JsonObject, cache: WeakMap
|
|
|
1040
1071
|
const child = schemaMap[key];
|
|
1041
1072
|
const next = normalizeOpenAIResponsesSchemaNode(child, cache);
|
|
1042
1073
|
if (next !== child) changed = true;
|
|
1074
|
+
if (stripUnsupportedRegexKeys && hasOpenAIUnsupportedRegexLookaround(key)) {
|
|
1075
|
+
changed = true;
|
|
1076
|
+
appendOpenAIResponsesFallbackPatternProperty(output, next);
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1043
1079
|
output[key] = next;
|
|
1044
1080
|
}
|
|
1045
1081
|
return changed ? output : schemaMap;
|
|
1046
1082
|
}
|
|
1047
1083
|
|
|
1084
|
+
function appendOpenAIResponsesFallbackPatternProperty(output: JsonObject, schema: unknown): void {
|
|
1085
|
+
const existing = output[OPENAI_RESPONSES_PATTERN_PROPERTIES_FALLBACK];
|
|
1086
|
+
if (existing === undefined) {
|
|
1087
|
+
output[OPENAI_RESPONSES_PATTERN_PROPERTIES_FALLBACK] = schema;
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
if (isJsonObject(existing) && Array.isArray(existing.anyOf) && Object.keys(existing).length === 1) {
|
|
1091
|
+
existing.anyOf = [...existing.anyOf, schema];
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
output[OPENAI_RESPONSES_PATTERN_PROPERTIES_FALLBACK] = { anyOf: [existing, schema] };
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1048
1097
|
// ---------------------------------------------------------------------------
|
|
1049
1098
|
// OpenAI strict mode — sanitize + enforce
|
|
1050
1099
|
// ---------------------------------------------------------------------------
|