pi-sap-aicore 0.3.2 → 0.3.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 +17 -1
- package/README.md +24 -3
- package/package.json +5 -4
- package/scripts/validate-foundation-executables.mjs +178 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.3] - 2026-07-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added `npm run validate:foundation`, a live SAP AI Core validation matrix for
|
|
15
|
+
direct foundation executables. It verifies text generation, real tool execution
|
|
16
|
+
side effects, and image input across Azure OpenAI, AWS Bedrock, and GCP Vertex
|
|
17
|
+
AI foundation routes.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Raised SAP AI SDK dependency floors to `^2.12.0` so fresh installs prefer the
|
|
22
|
+
patched transitive `axios` / `form-data` tree that resolves the npm audit
|
|
23
|
+
advisory for `form-data <4.0.6`.
|
|
24
|
+
|
|
10
25
|
## [0.3.2] - 2026-07-01
|
|
11
26
|
|
|
12
27
|
### Fixed
|
|
@@ -146,7 +161,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
146
161
|
`reasoning_effort` for OpenAI).
|
|
147
162
|
- MIT license and npm packaging.
|
|
148
163
|
|
|
149
|
-
[Unreleased]: https://github.com/ttiimmaahh/pi-sap-aicore/compare/v0.3.
|
|
164
|
+
[Unreleased]: https://github.com/ttiimmaahh/pi-sap-aicore/compare/v0.3.3...HEAD
|
|
165
|
+
[0.3.3]: https://github.com/ttiimmaahh/pi-sap-aicore/compare/v0.3.2...v0.3.3
|
|
150
166
|
[0.3.2]: https://github.com/ttiimmaahh/pi-sap-aicore/compare/v0.3.1...v0.3.2
|
|
151
167
|
[0.3.1]: https://github.com/ttiimmaahh/pi-sap-aicore/compare/v0.3.0...v0.3.1
|
|
152
168
|
[0.3.0]: https://github.com/ttiimmaahh/pi-sap-aicore/compare/v0.2.2...v0.3.0
|
package/README.md
CHANGED
|
@@ -160,6 +160,26 @@ there. An id with no matching deployment 404s at call time. Run
|
|
|
160
160
|
`/sap-models discover` in pi (or `node scripts/list-sap-models.mjs` from this
|
|
161
161
|
repo) to see what your tenant actually deploys.
|
|
162
162
|
|
|
163
|
+
### Live foundation validation
|
|
164
|
+
|
|
165
|
+
To validate normal coding-agent behavior across the three direct foundation
|
|
166
|
+
executables, run the live smoke-test matrix from this repo:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npm run validate:foundation
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The script uses `pi --no-extensions -e ./index.ts` so it tests the local checkout,
|
|
173
|
+
not a globally installed package. It makes real SAP model calls and validates:
|
|
174
|
+
|
|
175
|
+
- text generation
|
|
176
|
+
- real tool execution via side-effect files
|
|
177
|
+
- image input smoke tests
|
|
178
|
+
|
|
179
|
+
Default models are `gpt-5.5`, `anthropic--claude-4.8-opus`, and
|
|
180
|
+
`gemini-3.5-flash`. Override them with `GPT_MODEL`, `BEDROCK_MODEL`, and
|
|
181
|
+
`VERTEX_MODEL`; set `SKIP_IMAGE=1` to skip vision tests.
|
|
182
|
+
|
|
163
183
|
## Models
|
|
164
184
|
|
|
165
185
|
The model list is composed of three sources, merged at startup:
|
|
@@ -450,9 +470,10 @@ npmjs.com:
|
|
|
450
470
|
│ └── publish.yml # tag-driven npm publish via OIDC trusted publishing
|
|
451
471
|
├── index.ts # ExtensionAPI factory + registerProvider calls (both providers)
|
|
452
472
|
├── scripts/
|
|
453
|
-
│ ├── update-models.mjs
|
|
454
|
-
│ ├── list-sap-models.mjs
|
|
455
|
-
│
|
|
473
|
+
│ ├── update-models.mjs # maintainer script: fetches models.dev, writes models-snapshot.json
|
|
474
|
+
│ ├── list-sap-models.mjs # lists models your tenant actually deploys (diff vs snapshot)
|
|
475
|
+
│ ├── diagnose-streaming.mjs # probes orchestration streaming support per model
|
|
476
|
+
│ └── validate-foundation-executables.mjs # live text/tool/image smoke tests for direct foundation executables
|
|
456
477
|
└── src/
|
|
457
478
|
├── auth.ts # service-key validation + pi oauth registration
|
|
458
479
|
├── model-catalog.ts # loads snapshot/cache/overlay and adapts models.dev metadata
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-sap-aicore",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "SAP AI Core (orchestration + foundation) provider for the pi coding agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Tim Pearson (https://github.com/ttiimmaahh)",
|
|
@@ -31,12 +31,13 @@
|
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"update-models": "node scripts/update-models.mjs",
|
|
34
|
+
"validate:foundation": "node scripts/validate-foundation-executables.mjs",
|
|
34
35
|
"prepublishOnly": "tsc --noEmit"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
37
|
-
"@sap-ai-sdk/ai-api": "^2.
|
|
38
|
-
"@sap-ai-sdk/foundation-models": "^2.
|
|
39
|
-
"@sap-ai-sdk/orchestration": "^2.
|
|
38
|
+
"@sap-ai-sdk/ai-api": "^2.12.0",
|
|
39
|
+
"@sap-ai-sdk/foundation-models": "^2.12.0",
|
|
40
|
+
"@sap-ai-sdk/orchestration": "^2.12.0"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
43
|
"@earendil-works/pi-ai": "*",
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Live validation for SAP AI Core direct foundation executables.
|
|
3
|
+
//
|
|
4
|
+
// This script makes real, billed model calls through pi using the local extension.
|
|
5
|
+
// It validates the three high-value foundation executable families:
|
|
6
|
+
// - azure-openai → GPT/OpenAI models
|
|
7
|
+
// - aws-bedrock → Anthropic/Claude models
|
|
8
|
+
// - gcp-vertexai → Gemini models
|
|
9
|
+
//
|
|
10
|
+
// Usage from repo root:
|
|
11
|
+
// node scripts/validate-foundation-executables.mjs
|
|
12
|
+
//
|
|
13
|
+
// Optional model overrides:
|
|
14
|
+
// GPT_MODEL=gpt-5.5 \
|
|
15
|
+
// BEDROCK_MODEL=anthropic--claude-4.8-opus \
|
|
16
|
+
// VERTEX_MODEL=gemini-3.5-flash \
|
|
17
|
+
// node scripts/validate-foundation-executables.mjs
|
|
18
|
+
//
|
|
19
|
+
// Optional: SKIP_IMAGE=1 to skip vision tests.
|
|
20
|
+
|
|
21
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { tmpdir } from "node:os";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { spawnSync } from "node:child_process";
|
|
25
|
+
|
|
26
|
+
const ROOT = new URL("..", import.meta.url).pathname;
|
|
27
|
+
const PI = process.env.PI_BIN ?? "pi";
|
|
28
|
+
const SKIP_IMAGE = process.env.SKIP_IMAGE === "1";
|
|
29
|
+
|
|
30
|
+
const SCENARIOS = [
|
|
31
|
+
{
|
|
32
|
+
name: "azure-openai/gpt",
|
|
33
|
+
model: process.env.GPT_MODEL ?? "gpt-5.5",
|
|
34
|
+
marker: "GPT_TOOL_OK",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "aws-bedrock/anthropic",
|
|
38
|
+
model: process.env.BEDROCK_MODEL ?? "anthropic--claude-4.8-opus",
|
|
39
|
+
marker: "BEDROCK_TOOL_OK",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "gcp-vertexai/gemini",
|
|
43
|
+
model: process.env.VERTEX_MODEL ?? "gemini-3.5-flash",
|
|
44
|
+
marker: "VERTEX_TOOL_OK",
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const tinyPng = Buffer.from(
|
|
49
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=",
|
|
50
|
+
"base64",
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
function runPi(args, options = {}) {
|
|
54
|
+
const result = spawnSync(PI, args, {
|
|
55
|
+
cwd: ROOT,
|
|
56
|
+
encoding: "utf8",
|
|
57
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
58
|
+
...options,
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
status: result.status ?? 1,
|
|
62
|
+
stdout: result.stdout ?? "",
|
|
63
|
+
stderr: result.stderr ?? "",
|
|
64
|
+
combined: `${result.stdout ?? ""}${result.stderr ?? ""}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function baseArgs(model) {
|
|
69
|
+
return [
|
|
70
|
+
"--no-extensions",
|
|
71
|
+
"-e",
|
|
72
|
+
"./index.ts",
|
|
73
|
+
"--model",
|
|
74
|
+
`sap-aicore-foundation/${model}`,
|
|
75
|
+
"--no-context-files",
|
|
76
|
+
"--no-skills",
|
|
77
|
+
"-p",
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function assert(condition, message, detail) {
|
|
82
|
+
if (condition) return;
|
|
83
|
+
console.error(`\n❌ ${message}`);
|
|
84
|
+
if (detail) console.error(detail);
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
throw new Error(message);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function truncate(s, max = 2000) {
|
|
90
|
+
return s.length > max ? `${s.slice(0, max)}…[+${s.length - max} chars]` : s;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function validateText(scenario) {
|
|
94
|
+
const prompt = "Reply with exactly the single token: TEXT_OK";
|
|
95
|
+
const result = runPi([...baseArgs(scenario.model), prompt]);
|
|
96
|
+
assert(
|
|
97
|
+
result.status === 0,
|
|
98
|
+
`${scenario.name} text scenario exited ${result.status}`,
|
|
99
|
+
truncate(result.combined),
|
|
100
|
+
);
|
|
101
|
+
assert(
|
|
102
|
+
/TEXT_OK/.test(result.combined),
|
|
103
|
+
`${scenario.name} text scenario did not contain TEXT_OK`,
|
|
104
|
+
truncate(result.combined),
|
|
105
|
+
);
|
|
106
|
+
console.log(` ✓ text generation`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function validateToolUse(scenario, workDir) {
|
|
110
|
+
const file = join(workDir, `${scenario.name.replaceAll("/", "-")}.txt`);
|
|
111
|
+
const prompt =
|
|
112
|
+
`Use the bash tool to run exactly: printf ${scenario.marker} > ${file}. ` +
|
|
113
|
+
"Then say done.";
|
|
114
|
+
const result = runPi([...baseArgs(scenario.model), prompt]);
|
|
115
|
+
assert(
|
|
116
|
+
result.status === 0,
|
|
117
|
+
`${scenario.name} tool scenario exited ${result.status}`,
|
|
118
|
+
truncate(result.combined),
|
|
119
|
+
);
|
|
120
|
+
let actual = "";
|
|
121
|
+
try {
|
|
122
|
+
actual = readFileSync(file, "utf8");
|
|
123
|
+
} catch {
|
|
124
|
+
// handled below
|
|
125
|
+
}
|
|
126
|
+
assert(
|
|
127
|
+
actual === scenario.marker,
|
|
128
|
+
`${scenario.name} tool scenario did not create expected side-effect`,
|
|
129
|
+
`expected file ${file} to contain ${scenario.marker}\nmodel output:\n${truncate(result.combined)}`,
|
|
130
|
+
);
|
|
131
|
+
console.log(` ✓ tool execution side effect`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function validateImage(scenario, workDir) {
|
|
135
|
+
const imagePath = join(workDir, "tiny.png");
|
|
136
|
+
writeFileSync(imagePath, tinyPng);
|
|
137
|
+
const prompt =
|
|
138
|
+
"Look at @" +
|
|
139
|
+
imagePath +
|
|
140
|
+
" and reply with exactly IMAGE_OK if you can inspect the attached image.";
|
|
141
|
+
const result = runPi([...baseArgs(scenario.model), prompt]);
|
|
142
|
+
assert(
|
|
143
|
+
result.status === 0,
|
|
144
|
+
`${scenario.name} image scenario exited ${result.status}`,
|
|
145
|
+
truncate(result.combined),
|
|
146
|
+
);
|
|
147
|
+
assert(
|
|
148
|
+
/IMAGE_OK/.test(result.combined),
|
|
149
|
+
`${scenario.name} image scenario did not contain IMAGE_OK`,
|
|
150
|
+
truncate(result.combined),
|
|
151
|
+
);
|
|
152
|
+
console.log(` ✓ image input smoke test`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const workDir = mkdtempSync(join(tmpdir(), "pi-sap-aicore-foundation-"));
|
|
156
|
+
console.log(`Live SAP AI Core foundation validation`);
|
|
157
|
+
console.log(`repo: ${ROOT}`);
|
|
158
|
+
console.log(`workDir: ${workDir}`);
|
|
159
|
+
console.log(`skip image: ${SKIP_IMAGE ? "yes" : "no"}\n`);
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
for (const scenario of SCENARIOS) {
|
|
163
|
+
console.log(`${scenario.name} model=${scenario.model}`);
|
|
164
|
+
validateText(scenario);
|
|
165
|
+
validateToolUse(scenario, workDir);
|
|
166
|
+
if (!SKIP_IMAGE) validateImage(scenario, workDir);
|
|
167
|
+
console.log("");
|
|
168
|
+
}
|
|
169
|
+
console.log("✅ all foundation executable scenarios passed");
|
|
170
|
+
} catch {
|
|
171
|
+
// assert() already printed details and set exitCode.
|
|
172
|
+
} finally {
|
|
173
|
+
if (process.env.KEEP_VALIDATION_ARTIFACTS !== "1") {
|
|
174
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
175
|
+
} else {
|
|
176
|
+
console.log(`kept artifacts: ${workDir}`);
|
|
177
|
+
}
|
|
178
|
+
}
|