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 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.2...HEAD
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 # maintainer script: fetches models.dev, writes models-snapshot.json
454
- │ ├── list-sap-models.mjs # lists models your tenant actually deploys (diff vs snapshot)
455
- └── diagnose-streaming.mjs # probes orchestration streaming support per model
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.2",
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.10.0",
38
- "@sap-ai-sdk/foundation-models": "^2.10.0",
39
- "@sap-ai-sdk/orchestration": "^2.10.0"
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
+ }