@mcp-abap-adt/llm-agent-server 28.0.0 → 29.0.0
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/README.md +44 -4
- package/dist/composition/model-resolver.d.ts +2 -2
- package/dist/composition/model-resolver.d.ts.map +1 -1
- package/dist/composition/model-resolver.js +7 -6
- package/dist/composition/model-resolver.js.map +1 -1
- package/package.json +30 -33
- package/bin/llm-agent-check +0 -2
- package/dist/smart-agent/check-models-cli.d.ts +0 -14
- package/dist/smart-agent/check-models-cli.d.ts.map +0 -1
- package/dist/smart-agent/check-models-cli.js +0 -237
- package/dist/smart-agent/check-models-cli.js.map +0 -1
- package/tools/claude-via-agent.ps1 +0 -84
- package/tools/claude-via-agent.sh +0 -96
package/README.md
CHANGED
|
@@ -16,11 +16,51 @@ Importing from `@mcp-abap-adt/llm-agent-server` as a library is not supported as
|
|
|
16
16
|
|
|
17
17
|
(... existing binary documentation continues below ...)
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## CLI shipped
|
|
20
20
|
|
|
21
|
-
- `llm-agent` —
|
|
22
|
-
-
|
|
23
|
-
|
|
21
|
+
- `llm-agent` — the runtime (`llm-agent --config smart-server.yaml`). It is the only command a
|
|
22
|
+
global install (`npm i -g @mcp-abap-adt/llm-agent-server`) puts on your PATH.
|
|
23
|
+
|
|
24
|
+
### Repository tools (not published)
|
|
25
|
+
|
|
26
|
+
Two helpers live in the source repository and run from a checkout only:
|
|
27
|
+
|
|
28
|
+
| From the repo root | Does |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `npm run models:check` | reports which SAP AI Core models work for chat and which for embeddings |
|
|
31
|
+
| `npm run claude:via-agent` | starts `llm-agent` and points the Claude CLI at it (`npm run build` first) |
|
|
32
|
+
|
|
33
|
+
`models:check` probes through the server's own providers (`SapCoreAIProvider`,
|
|
34
|
+
`SapAiCoreEmbedder`) and reads the account by the server's credential rule, so a model it marks ✓
|
|
35
|
+
works in the server with the same settings. Every model is probed in both modes; the output is a
|
|
36
|
+
capability matrix:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
Model Chat Embed
|
|
40
|
+
gpt-5 ✓ 2007ms ✗
|
|
41
|
+
text-embedding-3-small ✗ ✓ 1536 dimensions
|
|
42
|
+
nvidia--llama-3.2-nv-embedqa-1b ✗ ✗
|
|
43
|
+
↳ embed declared, but failed: HTTP 400: Embedding Module: Model 'nvidia--llama-3.2-nv-embedqa-1b' requires 'type' …
|
|
44
|
+
Models: 3 Chat: 1/3 Embed: 1/3 Failed: 1
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Option (after `npm run models:check --`) | Effect |
|
|
48
|
+
|---|---|
|
|
49
|
+
| *(none)* | every model in the AI Core `foundation-models` catalog |
|
|
50
|
+
| `gpt-4o text-embedding-3-small` | only the named models |
|
|
51
|
+
| `--chat` / `--embed` | one mode only |
|
|
52
|
+
| `--config <yaml>` | the SAP AI Core models a server config uses, each in the mode of its role, with that role's `credentialRef` and the `temperature` / `maxTokens` the server would send (a classifier derived from `main` at `classifierTemperature` is its own row); non-SAP roles are listed as not checked |
|
|
53
|
+
| `--credential-ref <REF>` | the account: reads `<REF>_SERVICE_KEY` (default `LLM` → `LLM_SERVICE_KEY`) |
|
|
54
|
+
| `--env-path <file>` | the env file to load (default `.env` in the current directory) |
|
|
55
|
+
| `--resource-group <rg>` | AI Core resource group (default `$SAP_AI_RESOURCE_GROUP`, else `default`) |
|
|
56
|
+
| `--embed-scenario <s>` | `orchestration` (default) or `foundation-models` |
|
|
57
|
+
| `--timeout <ms>` / `--delay <ms>` | per-call timeout (default 60000) / pause between calls (default 2000) |
|
|
58
|
+
| `--version` / `--help` | package version / usage |
|
|
59
|
+
|
|
60
|
+
A ✗ in a mode the catalog does not declare for the model (an embedder under **Chat**) is expected
|
|
61
|
+
and printed without a reason. A reason line appears when a declared mode fails, or for a model the
|
|
62
|
+
catalog does not list. The exit code is `1` when such a failure exists. Unknown flags are rejected,
|
|
63
|
+
and a missing account is an error naming the variable to set.
|
|
24
64
|
|
|
25
65
|
See the repo docs for architecture, pipeline configuration and deployment:
|
|
26
66
|
[`docs/ARCHITECTURE.md`](https://github.com/fr0ster/llm-agent/blob/main/docs/ARCHITECTURE.md), [`docs/PIPELINES.md`](https://github.com/fr0ster/llm-agent/blob/main/docs/PIPELINES.md), [`docs/DEPLOYMENT.md`](https://github.com/fr0ster/llm-agent/blob/main/docs/DEPLOYMENT.md).
|
|
@@ -4,8 +4,8 @@ import { type SmartServerConfig, type SmartServerLlmConfig } from '@mcp-abap-adt
|
|
|
4
4
|
* `IModelResolver` for `PUT /v1/config`: the role's own `llm:` entry — its
|
|
5
5
|
* provider and its `credentialRef` — with the model swapped, built through the
|
|
6
6
|
* same `makeLlm` seam. The temperatures are this root's choice, read from the
|
|
7
|
-
* entries the way `SmartServer` reads them at startup
|
|
8
|
-
*
|
|
7
|
+
* entries the way `SmartServer` reads them at startup: an unset one stays
|
|
8
|
+
* unset, so the model's own default applies (§8 item 2). `undefined` without an `llm:` section, so a
|
|
9
9
|
* model update keeps being refused with 400.
|
|
10
10
|
*/
|
|
11
11
|
export declare function createModelResolver(makeLlm: (cfg: SmartServerLlmConfig) => Promise<ILlm>, llm: SmartServerConfig['llm']): IModelResolver | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-resolver.d.ts","sourceRoot":"","sources":["../../src/composition/model-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"model-resolver.d.ts","sourceRoot":"","sources":["../../src/composition/model-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,qCAAqC,CAAC;AAE7C;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,CAAC,GAAG,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,EACrD,GAAG,EAAE,iBAAiB,CAAC,KAAK,CAAC,GAC5B,cAAc,GAAG,SAAS,CA6B5B"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { normalizeLlmConfig, } from '@mcp-abap-adt/llm-agent-server-libs';
|
|
1
|
+
import { normalizeLlmConfig, optionalNumber, } from '@mcp-abap-adt/llm-agent-server-libs';
|
|
2
2
|
/**
|
|
3
3
|
* `IModelResolver` for `PUT /v1/config`: the role's own `llm:` entry — its
|
|
4
4
|
* provider and its `credentialRef` — with the model swapped, built through the
|
|
5
5
|
* same `makeLlm` seam. The temperatures are this root's choice, read from the
|
|
6
|
-
* entries the way `SmartServer` reads them at startup
|
|
7
|
-
*
|
|
6
|
+
* entries the way `SmartServer` reads them at startup: an unset one stays
|
|
7
|
+
* unset, so the model's own default applies (§8 item 2). `undefined` without an `llm:` section, so a
|
|
8
8
|
* model update keeps being refused with 400.
|
|
9
9
|
*/
|
|
10
10
|
export function createModelResolver(makeLlm, llm) {
|
|
@@ -24,13 +24,14 @@ export function createModelResolver(makeLlm, llm) {
|
|
|
24
24
|
const base = own ?? map.main;
|
|
25
25
|
// A declared classifier is built as written at startup (B12), so its
|
|
26
26
|
// temperature passes through unchanged here too — a swap must not move it.
|
|
27
|
+
// Unset stays unset: the provider sends none, the model's default applies.
|
|
27
28
|
const temperature = role === 'main'
|
|
28
|
-
?
|
|
29
|
+
? optionalNumber(map.main.temperature)
|
|
29
30
|
: role === 'classifier'
|
|
30
31
|
? own
|
|
31
32
|
? own.temperature
|
|
32
|
-
:
|
|
33
|
-
:
|
|
33
|
+
: optionalNumber(map.main.classifierTemperature)
|
|
34
|
+
: optionalNumber(base.temperature);
|
|
34
35
|
return makeLlm({ ...base, model: modelName, temperature });
|
|
35
36
|
},
|
|
36
37
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-resolver.js","sourceRoot":"","sources":["../../src/composition/model-resolver.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,
|
|
1
|
+
{"version":3,"file":"model-resolver.js","sourceRoot":"","sources":["../../src/composition/model-resolver.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,cAAc,GAGf,MAAM,qCAAqC,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAqD,EACrD,GAA6B;IAE7B,MAAM,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,OAAO;QACL,OAAO,CAAC,SAAS,EAAE,IAAI;YACrB,uEAAuE;YACvE,0EAA0E;YAC1E,sCAAsC;YACtC,MAAM,GAAG,GACP,IAAI,KAAK,QAAQ;gBACf,CAAC,CAAC,GAAG,CAAC,MAAM;gBACZ,CAAC,CAAC,IAAI,KAAK,YAAY;oBACrB,CAAC,CAAC,GAAG,CAAC,UAAU;oBAChB,CAAC,CAAC,SAAS,CAAC;YAClB,MAAM,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC;YAC7B,qEAAqE;YACrE,2EAA2E;YAC3E,2EAA2E;YAC3E,MAAM,WAAW,GACf,IAAI,KAAK,MAAM;gBACb,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC;gBACtC,CAAC,CAAC,IAAI,KAAK,YAAY;oBACrB,CAAC,CAAC,GAAG;wBACH,CAAC,CAAC,GAAG,CAAC,WAAW;wBACjB,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC;oBAClD,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACzC,OAAO,OAAO,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7D,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/llm-agent-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "29.0.0",
|
|
4
4
|
"description": "Default SmartAgent implementation with LLM providers, MCP client, HTTP server, and CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"llm-agent": "bin/llm-agent"
|
|
8
|
-
"llm-agent-check": "bin/llm-agent-check",
|
|
9
|
-
"claude-via-agent": "tools/claude-via-agent.sh"
|
|
7
|
+
"llm-agent": "bin/llm-agent"
|
|
10
8
|
},
|
|
11
9
|
"exports": {
|
|
12
10
|
"./package.json": "./package.json"
|
|
@@ -14,14 +12,13 @@
|
|
|
14
12
|
"files": [
|
|
15
13
|
"dist",
|
|
16
14
|
"bin",
|
|
17
|
-
"tools",
|
|
18
15
|
"README.md",
|
|
19
16
|
"LICENSE"
|
|
20
17
|
],
|
|
21
18
|
"scripts": {
|
|
22
19
|
"build": "tsc -p tsconfig.json",
|
|
23
20
|
"clean": "tsc -p tsconfig.json --clean",
|
|
24
|
-
"test": "node --import tsx/esm --test --test-reporter=spec 'src/**/*.test.ts'",
|
|
21
|
+
"test": "node --import tsx/esm --test --test-reporter=spec 'src/**/*.test.ts' 'scripts/**/*.test.ts'",
|
|
25
22
|
"test:adapters": "node --import tsx/esm --test --test-reporter=spec src/smart-agent/adapters/__tests__/llm-adapter.test.ts",
|
|
26
23
|
"test:server": "node --import tsx/esm --test --test-reporter=spec src/smart-agent/__tests__/server.test.ts",
|
|
27
24
|
"smoke:adapters": "tsx src/smoke-adapters.ts",
|
|
@@ -31,41 +28,41 @@
|
|
|
31
28
|
"release:check": "tsc -p tsconfig.json --noEmit"
|
|
32
29
|
},
|
|
33
30
|
"dependencies": {
|
|
34
|
-
"@mcp-abap-adt/anthropic-llm": "^
|
|
35
|
-
"@mcp-abap-adt/deepseek-llm": "^
|
|
36
|
-
"@mcp-abap-adt/hana-vector-rag": "^
|
|
31
|
+
"@mcp-abap-adt/anthropic-llm": "^29.0.0",
|
|
32
|
+
"@mcp-abap-adt/deepseek-llm": "^29.0.0",
|
|
33
|
+
"@mcp-abap-adt/hana-vector-rag": "^29.0.0",
|
|
37
34
|
"@mcp-abap-adt/interfaces-auth": "^2.1.0",
|
|
38
35
|
"@mcp-abap-adt/interfaces-utils": "^1.1.0",
|
|
39
|
-
"@mcp-abap-adt/llm-agent": "^
|
|
40
|
-
"@mcp-abap-adt/llm-agent-libs": "^
|
|
41
|
-
"@mcp-abap-adt/llm-agent-mcp": "^
|
|
42
|
-
"@mcp-abap-adt/llm-agent-rag": "^
|
|
43
|
-
"@mcp-abap-adt/llm-agent-server-libs": "^
|
|
44
|
-
"@mcp-abap-adt/ollama-embedder": "^
|
|
45
|
-
"@mcp-abap-adt/ollama-llm": "^
|
|
46
|
-
"@mcp-abap-adt/openai-embedder": "^
|
|
47
|
-
"@mcp-abap-adt/openai-llm": "^
|
|
48
|
-
"@mcp-abap-adt/pg-vector-rag": "^
|
|
49
|
-
"@mcp-abap-adt/qdrant-rag": "^
|
|
50
|
-
"@mcp-abap-adt/sap-aicore-auth": "^
|
|
51
|
-
"@mcp-abap-adt/sap-aicore-embedder": "^
|
|
52
|
-
"@mcp-abap-adt/sap-aicore-llm": "^
|
|
36
|
+
"@mcp-abap-adt/llm-agent": "^29.0.0",
|
|
37
|
+
"@mcp-abap-adt/llm-agent-libs": "^29.0.0",
|
|
38
|
+
"@mcp-abap-adt/llm-agent-mcp": "^29.0.0",
|
|
39
|
+
"@mcp-abap-adt/llm-agent-rag": "^29.0.0",
|
|
40
|
+
"@mcp-abap-adt/llm-agent-server-libs": "^29.0.0",
|
|
41
|
+
"@mcp-abap-adt/ollama-embedder": "^29.0.0",
|
|
42
|
+
"@mcp-abap-adt/ollama-llm": "^29.0.0",
|
|
43
|
+
"@mcp-abap-adt/openai-embedder": "^29.0.0",
|
|
44
|
+
"@mcp-abap-adt/openai-llm": "^29.0.0",
|
|
45
|
+
"@mcp-abap-adt/pg-vector-rag": "^29.0.0",
|
|
46
|
+
"@mcp-abap-adt/qdrant-rag": "^29.0.0",
|
|
47
|
+
"@mcp-abap-adt/sap-aicore-auth": "^29.0.0",
|
|
48
|
+
"@mcp-abap-adt/sap-aicore-embedder": "^29.0.0",
|
|
49
|
+
"@mcp-abap-adt/sap-aicore-llm": "^29.0.0",
|
|
53
50
|
"@modelcontextprotocol/sdk": "^1.30.1",
|
|
54
51
|
"dotenv": "^17.3.1",
|
|
55
52
|
"yaml": "^2.9.1",
|
|
56
53
|
"zod": "^4.6.5"
|
|
57
54
|
},
|
|
58
55
|
"devDependencies": {
|
|
59
|
-
"@mcp-abap-adt/anthropic-llm": "^
|
|
60
|
-
"@mcp-abap-adt/deepseek-llm": "^
|
|
61
|
-
"@mcp-abap-adt/hana-vector-rag": "^
|
|
62
|
-
"@mcp-abap-adt/ollama-embedder": "^
|
|
63
|
-
"@mcp-abap-adt/openai-embedder": "^
|
|
64
|
-
"@mcp-abap-adt/openai-llm": "^
|
|
65
|
-
"@mcp-abap-adt/pg-vector-rag": "^
|
|
66
|
-
"@mcp-abap-adt/qdrant-rag": "^
|
|
67
|
-
"@mcp-abap-adt/sap-aicore-embedder": "^
|
|
68
|
-
"@mcp-abap-adt/sap-aicore-llm": "^
|
|
56
|
+
"@mcp-abap-adt/anthropic-llm": "^29.0.0",
|
|
57
|
+
"@mcp-abap-adt/deepseek-llm": "^29.0.0",
|
|
58
|
+
"@mcp-abap-adt/hana-vector-rag": "^29.0.0",
|
|
59
|
+
"@mcp-abap-adt/ollama-embedder": "^29.0.0",
|
|
60
|
+
"@mcp-abap-adt/openai-embedder": "^29.0.0",
|
|
61
|
+
"@mcp-abap-adt/openai-llm": "^29.0.0",
|
|
62
|
+
"@mcp-abap-adt/pg-vector-rag": "^29.0.0",
|
|
63
|
+
"@mcp-abap-adt/qdrant-rag": "^29.0.0",
|
|
64
|
+
"@mcp-abap-adt/sap-aicore-embedder": "^29.0.0",
|
|
65
|
+
"@mcp-abap-adt/sap-aicore-llm": "^29.0.0",
|
|
69
66
|
"@opentelemetry/api": "^1.9.1",
|
|
70
67
|
"@sap-ai-sdk/ai-api": "^2.16.0",
|
|
71
68
|
"@sap-ai-sdk/orchestration": "^2.16.0",
|
package/bin/llm-agent-check
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* llm-agent-check — Verify which SAP AI Core models actually respond.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* llm-agent-check # check ALL models from SDK catalog
|
|
7
|
-
* llm-agent-check anthropic--claude-4.6-sonnet # check one model
|
|
8
|
-
* llm-agent-check gpt-4o anthropic--claude-4.5-haiku # check specific models
|
|
9
|
-
* llm-agent-check --delay 5000 # custom rate limit delay (ms)
|
|
10
|
-
*
|
|
11
|
-
* Sends a minimal chat request to each model and reports OK/FAIL.
|
|
12
|
-
*/
|
|
13
|
-
export {};
|
|
14
|
-
//# sourceMappingURL=check-models-cli.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"check-models-cli.d.ts","sourceRoot":"","sources":["../../src/smart-agent/check-models-cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG"}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* llm-agent-check — Verify which SAP AI Core models actually respond.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* llm-agent-check # check ALL models from SDK catalog
|
|
7
|
-
* llm-agent-check anthropic--claude-4.6-sonnet # check one model
|
|
8
|
-
* llm-agent-check gpt-4o anthropic--claude-4.5-haiku # check specific models
|
|
9
|
-
* llm-agent-check --delay 5000 # custom rate limit delay (ms)
|
|
10
|
-
*
|
|
11
|
-
* Sends a minimal chat request to each model and reports OK/FAIL.
|
|
12
|
-
*/
|
|
13
|
-
import fs from 'node:fs';
|
|
14
|
-
import path from 'node:path';
|
|
15
|
-
import { parseArgs } from 'node:util';
|
|
16
|
-
import { configDotenv } from 'dotenv';
|
|
17
|
-
import { parse as parseYaml } from 'yaml';
|
|
18
|
-
configDotenv({ path: path.resolve('.env') });
|
|
19
|
-
const { values: args, positionals } = parseArgs({
|
|
20
|
-
options: {
|
|
21
|
-
config: { type: 'string', short: 'c' },
|
|
22
|
-
delay: { type: 'string', short: 'd' },
|
|
23
|
-
help: { type: 'boolean', short: 'h' },
|
|
24
|
-
},
|
|
25
|
-
allowPositionals: true,
|
|
26
|
-
strict: false,
|
|
27
|
-
});
|
|
28
|
-
if (args.help) {
|
|
29
|
-
process.stdout.write('Usage: llm-agent-check [model1 ...] [--config <yaml>] [--delay <ms>]\n\n' +
|
|
30
|
-
' No arguments Check ALL models from SAP AI Core catalog\n' +
|
|
31
|
-
' model1 model2 Check only specified models\n' +
|
|
32
|
-
' --config <yaml> -c Check models from pipeline YAML config\n' +
|
|
33
|
-
' --delay <ms> -d Delay between checks (default: 2000)\n');
|
|
34
|
-
process.exit(0);
|
|
35
|
-
}
|
|
36
|
-
const delayMs = Number(args.delay ?? 2000);
|
|
37
|
-
const requestedModels = positionals;
|
|
38
|
-
const configPath = args.config;
|
|
39
|
-
function resolveEnv(value) {
|
|
40
|
-
return value.replace(/\$\{([^:}]+)(?::-(.*?))?\}/g, (_, name, fallback) => process.env[name] ?? fallback ?? '');
|
|
41
|
-
}
|
|
42
|
-
function extractModelsFromYaml(yamlPath) {
|
|
43
|
-
if (!fs.existsSync(yamlPath)) {
|
|
44
|
-
process.stderr.write(`Config file not found: ${yamlPath}\n`);
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
// biome-ignore lint/suspicious/noExplicitAny: yaml structure
|
|
48
|
-
const yaml = parseYaml(fs.readFileSync(yamlPath, 'utf8'));
|
|
49
|
-
const entries = [];
|
|
50
|
-
// Pipeline LLM models (pipeline.llm.main/classifier/helper)
|
|
51
|
-
if (yaml.pipeline?.llm) {
|
|
52
|
-
for (const [role, cfg] of Object.entries(yaml.pipeline.llm)) {
|
|
53
|
-
if (cfg?.model) {
|
|
54
|
-
entries.push({ role, model: resolveEnv(String(cfg.model)) });
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// Simple LLM config (llm.model)
|
|
59
|
-
if (yaml.llm?.model && entries.length === 0) {
|
|
60
|
-
entries.push({ role: 'main', model: resolveEnv(String(yaml.llm.model)) });
|
|
61
|
-
}
|
|
62
|
-
// RAG embedder models
|
|
63
|
-
if (yaml.pipeline?.rag) {
|
|
64
|
-
for (const [store, cfg] of Object.entries(yaml.pipeline.rag)) {
|
|
65
|
-
if (cfg?.embedder?.model) {
|
|
66
|
-
entries.push({
|
|
67
|
-
role: `embedder:${store}`,
|
|
68
|
-
model: resolveEnv(String(cfg.embedder.model)),
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
else if (yaml.rag?.embedder?.model) {
|
|
74
|
-
entries.push({
|
|
75
|
-
role: 'embedder',
|
|
76
|
-
model: resolveEnv(String(yaml.rag.embedder.model)),
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
return entries;
|
|
80
|
-
}
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
// Fetch model catalog from SAP AI Core
|
|
83
|
-
// ---------------------------------------------------------------------------
|
|
84
|
-
async function fetchAllModels() {
|
|
85
|
-
try {
|
|
86
|
-
const { ScenarioApi } = await import('@sap-ai-sdk/ai-api');
|
|
87
|
-
const resourceGroup = process.env.SAP_AI_RESOURCE_GROUP || 'default';
|
|
88
|
-
const result = await ScenarioApi.scenarioQueryModels('foundation-models', {
|
|
89
|
-
'AI-Resource-Group': resourceGroup,
|
|
90
|
-
}).execute();
|
|
91
|
-
// biome-ignore lint/suspicious/noExplicitAny: SDK response shape
|
|
92
|
-
const resources = result.resources;
|
|
93
|
-
return resources.map((r) => r.model).sort();
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
process.stderr.write(`Failed to fetch model catalog: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async function checkLlm(model) {
|
|
101
|
-
const start = Date.now();
|
|
102
|
-
try {
|
|
103
|
-
const { OrchestrationClient } = await import('@sap-ai-sdk/orchestration');
|
|
104
|
-
const client = new OrchestrationClient({
|
|
105
|
-
promptTemplating: {
|
|
106
|
-
model: {
|
|
107
|
-
name: model,
|
|
108
|
-
params: { max_tokens: 10, temperature: 0 },
|
|
109
|
-
},
|
|
110
|
-
prompt: {
|
|
111
|
-
template: [{ role: 'user', content: 'Reply with OK' }],
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
const response = await client.chatCompletion();
|
|
116
|
-
const content = response.getContent() || '';
|
|
117
|
-
return {
|
|
118
|
-
ok: true,
|
|
119
|
-
detail: content.trim().slice(0, 30),
|
|
120
|
-
ms: Date.now() - start,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
catch (err) {
|
|
124
|
-
return { ok: false, detail: extractError(err), ms: Date.now() - start };
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
async function checkEmbedder(model) {
|
|
128
|
-
const start = Date.now();
|
|
129
|
-
try {
|
|
130
|
-
const { OrchestrationEmbeddingClient } = await import('@sap-ai-sdk/orchestration');
|
|
131
|
-
const client = new OrchestrationEmbeddingClient({
|
|
132
|
-
embeddings: {
|
|
133
|
-
model: {
|
|
134
|
-
name: model,
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
});
|
|
138
|
-
const response = await client.embed({ input: 'ping' });
|
|
139
|
-
const embeddings = response.getEmbeddings();
|
|
140
|
-
const dims = embeddings?.[0]?.embedding?.length ?? 0;
|
|
141
|
-
return {
|
|
142
|
-
ok: true,
|
|
143
|
-
detail: `${dims} dimensions`,
|
|
144
|
-
ms: Date.now() - start,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
catch (err) {
|
|
148
|
-
return { ok: false, detail: extractError(err), ms: Date.now() - start };
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
function extractError(err) {
|
|
152
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
153
|
-
const statusMatch = msg.match(/status code (\d+)/);
|
|
154
|
-
if (statusMatch)
|
|
155
|
-
return `HTTP ${statusMatch[1]}`;
|
|
156
|
-
// Capture nested cause
|
|
157
|
-
// biome-ignore lint/suspicious/noExplicitAny: error inspection
|
|
158
|
-
const cause = err?.cause?.message;
|
|
159
|
-
if (cause)
|
|
160
|
-
return `${msg.slice(0, 40)} — ${cause.slice(0, 40)}`;
|
|
161
|
-
return msg.slice(0, 80);
|
|
162
|
-
}
|
|
163
|
-
async function checkModel(model, isEmbedder) {
|
|
164
|
-
return isEmbedder ? checkEmbedder(model) : checkLlm(model);
|
|
165
|
-
}
|
|
166
|
-
let modelsToCheck;
|
|
167
|
-
let yamlEntries;
|
|
168
|
-
if (configPath) {
|
|
169
|
-
yamlEntries = extractModelsFromYaml(configPath);
|
|
170
|
-
// Deduplicate by model+type
|
|
171
|
-
const seen = new Set();
|
|
172
|
-
modelsToCheck = [];
|
|
173
|
-
for (const e of yamlEntries) {
|
|
174
|
-
const isEmbedder = e.role.startsWith('embedder');
|
|
175
|
-
const key = `${e.model}:${isEmbedder}`;
|
|
176
|
-
if (!seen.has(key)) {
|
|
177
|
-
seen.add(key);
|
|
178
|
-
modelsToCheck.push({ model: e.model, isEmbedder });
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
else if (requestedModels.length > 0) {
|
|
183
|
-
modelsToCheck = requestedModels.map((m) => ({ model: m, isEmbedder: false }));
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
const catalogModels = await fetchAllModels();
|
|
187
|
-
modelsToCheck = catalogModels.map((m) => ({ model: m, isEmbedder: false }));
|
|
188
|
-
}
|
|
189
|
-
if (configPath) {
|
|
190
|
-
// Read provider from YAML to warn about non-SAP models
|
|
191
|
-
const raw = parseYaml(fs.readFileSync(configPath, 'utf8'));
|
|
192
|
-
const pipeline = raw.pipeline;
|
|
193
|
-
const llmConfig = pipeline?.llm;
|
|
194
|
-
const mainProvider = llmConfig?.main?.provider;
|
|
195
|
-
const topProvider = raw.llm?.provider;
|
|
196
|
-
const provider = mainProvider ?? topProvider;
|
|
197
|
-
if (provider && provider !== 'sap-ai-sdk') {
|
|
198
|
-
process.stderr.write(`\n Warning: llm-agent-check uses SAP AI Core OrchestrationClient.\n` +
|
|
199
|
-
` Provider "${provider}" models are validated at startup via ILlm.chat() — no CLI check needed.\n\n`);
|
|
200
|
-
process.exit(0);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
process.stdout.write(`\n Checking ${modelsToCheck.length} model(s)...\n`);
|
|
204
|
-
process.stdout.write(' ─────────────────────────────────────────────────\n');
|
|
205
|
-
let passed = 0;
|
|
206
|
-
let failed = 0;
|
|
207
|
-
const results = [];
|
|
208
|
-
for (let i = 0; i < modelsToCheck.length; i++) {
|
|
209
|
-
const { model, isEmbedder } = modelsToCheck[i];
|
|
210
|
-
const result = await checkModel(model, isEmbedder);
|
|
211
|
-
results.push({ model, ok: result.ok });
|
|
212
|
-
if (result.ok)
|
|
213
|
-
passed++;
|
|
214
|
-
else
|
|
215
|
-
failed++;
|
|
216
|
-
const status = result.ok ? '\x1b[32m OK \x1b[0m' : '\x1b[31m FAIL \x1b[0m';
|
|
217
|
-
const ms = `${result.ms}ms`;
|
|
218
|
-
const typeLabel = isEmbedder ? '[embed]' : '[llm] ';
|
|
219
|
-
process.stdout.write(` ${typeLabel} ${model.padEnd(38)} ${status} ${ms.padStart(7)} ${result.detail}\n`);
|
|
220
|
-
if (i < modelsToCheck.length - 1) {
|
|
221
|
-
await new Promise((r) => setTimeout(r, delayMs));
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
process.stdout.write(' ─────────────────────────────────────────────────\n');
|
|
225
|
-
if (yamlEntries) {
|
|
226
|
-
process.stdout.write(` Config: ${configPath}\n`);
|
|
227
|
-
for (const entry of yamlEntries) {
|
|
228
|
-
const res = results.find((r) => r.model === entry.model);
|
|
229
|
-
const icon = res?.ok ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
230
|
-
process.stdout.write(` ${entry.role.padEnd(18)} ${entry.model.padEnd(40)} ${icon}\n`);
|
|
231
|
-
}
|
|
232
|
-
process.stdout.write(' ─────────────────────────────────────────────────\n');
|
|
233
|
-
}
|
|
234
|
-
process.stdout.write(` Total: ${modelsToCheck.length} \x1b[32mOK: ${passed}\x1b[0m \x1b[31mFAIL: ${failed}\x1b[0m\n\n`);
|
|
235
|
-
if (failed > 0)
|
|
236
|
-
process.exit(1);
|
|
237
|
-
//# sourceMappingURL=check-models-cli.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"check-models-cli.js","sourceRoot":"","sources":["../../src/smart-agent/check-models-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# Launch Claude CLI through llm-agent SmartServer.
|
|
2
|
-
#
|
|
3
|
-
# The server starts in the background, Claude CLI connects via ANTHROPIC_BASE_URL,
|
|
4
|
-
# and the server stops when Claude exits.
|
|
5
|
-
#
|
|
6
|
-
# Usage:
|
|
7
|
-
# .\tools\claude-via-agent.ps1 # uses defaults from .env
|
|
8
|
-
# $env:LLM_MODEL="gpt-4o"; .\tools\claude-via-agent.ps1 # override model
|
|
9
|
-
#
|
|
10
|
-
# Required environment (set in .env or before running):
|
|
11
|
-
# LLM_PROVIDER — openai | anthropic | deepseek | sap-ai-sdk
|
|
12
|
-
# LLM_API_KEY — provider API key (openai / anthropic / deepseek)
|
|
13
|
-
# LLM_SERVICE_KEY — the SAP AI Core service-key JSON (sap-ai-sdk)
|
|
14
|
-
# LLM_MODEL — model name as the provider expects
|
|
15
|
-
#
|
|
16
|
-
# Optional:
|
|
17
|
-
# MCP_ENDPOINT — MCP server URL (default: none)
|
|
18
|
-
# PORT — llm-agent port (default: 4004)
|
|
19
|
-
|
|
20
|
-
$ErrorActionPreference = "Stop"
|
|
21
|
-
|
|
22
|
-
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
23
|
-
$ProjectDir = Split-Path -Parent $ScriptDir
|
|
24
|
-
|
|
25
|
-
# Load .env if present
|
|
26
|
-
$EnvFile = Join-Path $ProjectDir ".env"
|
|
27
|
-
if (Test-Path $EnvFile) {
|
|
28
|
-
Get-Content $EnvFile | ForEach-Object {
|
|
29
|
-
$line = $_.Trim()
|
|
30
|
-
if ($line -and -not $line.StartsWith("#")) {
|
|
31
|
-
$parts = $line -split "=", 2
|
|
32
|
-
if ($parts.Length -eq 2) {
|
|
33
|
-
[Environment]::SetEnvironmentVariable($parts[0].Trim(), $parts[1].Trim(), "Process")
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
$Port = if ($env:PORT) { $env:PORT } else { "4004" }
|
|
40
|
-
$AgentProcess = $null
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
# Start llm-agent in background
|
|
44
|
-
Write-Host "Starting llm-agent on port $Port..."
|
|
45
|
-
$AgentProcess = Start-Process -FilePath "npx" -ArgumentList "llm-agent" `
|
|
46
|
-
-WorkingDirectory $ProjectDir -PassThru -NoNewWindow
|
|
47
|
-
|
|
48
|
-
# Wait for server to be ready
|
|
49
|
-
$ready = $false
|
|
50
|
-
for ($i = 0; $i -lt 30; $i++) {
|
|
51
|
-
if ($AgentProcess.HasExited) {
|
|
52
|
-
Write-Error "llm-agent failed to start"
|
|
53
|
-
exit 1
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
$null = Invoke-RestMethod -Uri "http://localhost:$Port/v1/models" -TimeoutSec 2
|
|
57
|
-
$ready = $true
|
|
58
|
-
break
|
|
59
|
-
} catch {
|
|
60
|
-
Start-Sleep -Seconds 1
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (-not $ready) {
|
|
65
|
-
Write-Error "llm-agent did not become ready in 30 seconds"
|
|
66
|
-
exit 1
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
Write-Host "llm-agent ready. Launching Claude CLI..."
|
|
70
|
-
Write-Host ""
|
|
71
|
-
|
|
72
|
-
# Launch Claude CLI pointing to llm-agent
|
|
73
|
-
# Note: do NOT set ANTHROPIC_API_KEY — it conflicts with claude.ai login.
|
|
74
|
-
# llm-agent does not validate the auth header; Claude CLI uses its own token.
|
|
75
|
-
$env:ANTHROPIC_BASE_URL = "http://localhost:$Port"
|
|
76
|
-
& claude @args
|
|
77
|
-
}
|
|
78
|
-
finally {
|
|
79
|
-
# Stop llm-agent
|
|
80
|
-
if ($AgentProcess -and -not $AgentProcess.HasExited) {
|
|
81
|
-
Stop-Process -Id $AgentProcess.Id -Force -ErrorAction SilentlyContinue
|
|
82
|
-
$AgentProcess.WaitForExit(5000) | Out-Null
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Launch Claude CLI through llm-agent SmartServer.
|
|
3
|
-
#
|
|
4
|
-
# Usage:
|
|
5
|
-
# ./tools/claude-via-agent.sh # auto-selects pipeline by LLM_PROVIDER
|
|
6
|
-
# ./tools/claude-via-agent.sh --config pipelines/deepseek.yaml # explicit pipeline
|
|
7
|
-
#
|
|
8
|
-
# All credentials in .env:
|
|
9
|
-
# LLM_PROVIDER — selects pipeline: pipelines/<provider>.yaml
|
|
10
|
-
# LLM_API_KEY — for deepseek/openai/anthropic pipelines
|
|
11
|
-
# LLM_SERVICE_KEY — for sap-ai-core pipeline (the service-key JSON)
|
|
12
|
-
# MCP_ENDPOINT — MCP server URL (optional)
|
|
13
|
-
# PORT — llm-agent port (default: 4004)
|
|
14
|
-
|
|
15
|
-
set -euo pipefail
|
|
16
|
-
|
|
17
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
-
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
19
|
-
|
|
20
|
-
PORT="${PORT:-4004}"
|
|
21
|
-
AGENT_PID=""
|
|
22
|
-
AGENT_STARTED_BY_US=false
|
|
23
|
-
|
|
24
|
-
# Select pipeline config: explicit --config, or by LLM_PROVIDER from env/.env
|
|
25
|
-
CONFIG=""
|
|
26
|
-
if [[ "${1:-}" == "--config" && -n "${2:-}" ]]; then
|
|
27
|
-
CONFIG="$2"
|
|
28
|
-
shift 2
|
|
29
|
-
else
|
|
30
|
-
# Read LLM_PROVIDER from .env if not in env
|
|
31
|
-
PROVIDER="${LLM_PROVIDER:-}"
|
|
32
|
-
if [[ -z "$PROVIDER" && -f "$PROJECT_DIR/.env" ]]; then
|
|
33
|
-
PROVIDER=$(grep "^LLM_PROVIDER=" "$PROJECT_DIR/.env" 2>/dev/null | cut -d= -f2)
|
|
34
|
-
fi
|
|
35
|
-
if [[ -n "$PROVIDER" && -f "$PROJECT_DIR/pipelines/${PROVIDER}.yaml" ]]; then
|
|
36
|
-
CONFIG="pipelines/${PROVIDER}.yaml"
|
|
37
|
-
fi
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
cleanup() {
|
|
41
|
-
if [[ "$AGENT_STARTED_BY_US" == true && -n "$AGENT_PID" ]]; then
|
|
42
|
-
kill "$AGENT_PID" 2>/dev/null || true
|
|
43
|
-
wait "$AGENT_PID" 2>/dev/null || true
|
|
44
|
-
echo "llm-agent stopped."
|
|
45
|
-
fi
|
|
46
|
-
}
|
|
47
|
-
trap cleanup EXIT INT TERM
|
|
48
|
-
|
|
49
|
-
# Check if llm-agent is already running on this port
|
|
50
|
-
if curl -sf "http://localhost:$PORT/health" >/dev/null 2>&1 || \
|
|
51
|
-
curl -sf "http://localhost:$PORT/v1/models" >/dev/null 2>&1; then
|
|
52
|
-
echo "llm-agent already running on port $PORT, reusing..."
|
|
53
|
-
else
|
|
54
|
-
echo "Starting llm-agent on port $PORT..."
|
|
55
|
-
[[ -n "$CONFIG" ]] && echo "Pipeline: $CONFIG"
|
|
56
|
-
cd "$PROJECT_DIR"
|
|
57
|
-
|
|
58
|
-
if [[ -n "$CONFIG" ]]; then
|
|
59
|
-
npx llm-agent --config "$CONFIG" &
|
|
60
|
-
else
|
|
61
|
-
npx llm-agent &
|
|
62
|
-
fi
|
|
63
|
-
AGENT_PID=$!
|
|
64
|
-
AGENT_STARTED_BY_US=true
|
|
65
|
-
|
|
66
|
-
for i in $(seq 1 30); do
|
|
67
|
-
if curl -sf "http://localhost:$PORT/health" >/dev/null 2>&1 || \
|
|
68
|
-
curl -sf "http://localhost:$PORT/v1/models" >/dev/null 2>&1; then
|
|
69
|
-
break
|
|
70
|
-
fi
|
|
71
|
-
if ! kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
72
|
-
echo "Error: llm-agent failed to start" >&2
|
|
73
|
-
exit 1
|
|
74
|
-
fi
|
|
75
|
-
sleep 1
|
|
76
|
-
done
|
|
77
|
-
|
|
78
|
-
if ! kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
79
|
-
echo "Error: llm-agent exited unexpectedly" >&2
|
|
80
|
-
exit 1
|
|
81
|
-
fi
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
echo "llm-agent ready. Launching Claude CLI..."
|
|
85
|
-
echo ""
|
|
86
|
-
|
|
87
|
-
# Claude CLI auth: if logged into claude.ai, do not set ANTHROPIC_API_KEY (conflicts).
|
|
88
|
-
CLAUDE_AUTH_FILE="${HOME}/.claude/.credentials.json"
|
|
89
|
-
if [[ -f "$CLAUDE_AUTH_FILE" ]] && grep -q "accessToken" "$CLAUDE_AUTH_FILE" 2>/dev/null; then
|
|
90
|
-
unset ANTHROPIC_API_KEY 2>/dev/null || true
|
|
91
|
-
else
|
|
92
|
-
export ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-placeholder}"
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
ANTHROPIC_BASE_URL="http://localhost:$PORT" \
|
|
96
|
-
claude "$@"
|