@super_studio/ecforce-ai-agent-server 1.2.0 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@super_studio/ecforce-ai-agent-server",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.cts",
@@ -18,6 +18,10 @@
18
18
  "require": "./dist/mcp-auth.cjs",
19
19
  "import": "./dist/mcp-auth.mjs"
20
20
  },
21
+ "./provider": {
22
+ "require": "./dist/provider.cjs",
23
+ "import": "./dist/provider.mjs"
24
+ },
21
25
  "./package.json": "./package.json"
22
26
  },
23
27
  "publishConfig": {
@@ -28,6 +32,10 @@
28
32
  "swagger-typescript-api": "13.2.9"
29
33
  },
30
34
  "dependencies": {
35
+ "@ai-sdk/anthropic": "^3.0.68",
36
+ "@ai-sdk/google-vertex": "^4.0.106",
37
+ "@ai-sdk/openai": "^3.0.49",
38
+ "@ai-sdk/provider": "^3.0.8",
31
39
  "@panva/hkdf": "^1.2.1",
32
40
  "jose": "^6.0.6"
33
41
  },
@@ -1 +1 @@
1
- export const API_ENDPOINT = "http://localhost:4043";
1
+ export const API_ENDPOINT = "https://agent.ec-force.com";
@@ -0,0 +1,64 @@
1
+ export function createGoogleProxyFetch(apiKey: string | undefined) {
2
+ return async (input: RequestInfo | URL, init?: RequestInit) => {
3
+ const url = typeof input === "string" ? input : input.toString();
4
+ const headers = new Headers(init?.headers);
5
+ headers.delete("x-goog-api-key");
6
+ headers.delete("X-Goog-Api-Key");
7
+
8
+ if (apiKey) {
9
+ headers.set("authorization", `Bearer ${apiKey}`);
10
+ }
11
+
12
+ const body = normalizeGoogleProxyRequestBody(url, init?.body);
13
+
14
+ return fetch(input, {
15
+ ...init,
16
+ ...(body ? { body } : {}),
17
+ headers,
18
+ });
19
+ };
20
+ }
21
+
22
+ function normalizeGoogleProxyRequestBody(
23
+ url: string,
24
+ body: RequestInit["body"],
25
+ ): RequestInit["body"] {
26
+ if (!body || typeof body !== "string") {
27
+ return body;
28
+ }
29
+
30
+ if (
31
+ !url.includes(":generateContent") &&
32
+ !url.includes(":streamGenerateContent")
33
+ ) {
34
+ return body;
35
+ }
36
+
37
+ try {
38
+ const payload = JSON.parse(body) as Record<string, unknown>;
39
+ const toolConfig = payload.toolConfig as
40
+ | Record<string, unknown>
41
+ | undefined;
42
+ const functionCallingConfig = toolConfig?.functionCallingConfig as
43
+ | Record<string, unknown>
44
+ | undefined;
45
+
46
+ if (!functionCallingConfig?.streamFunctionCallArguments) {
47
+ return body;
48
+ }
49
+
50
+ const {
51
+ streamFunctionCallArguments: _streamFunctionCallArguments,
52
+ ...restFunctionCallingConfig
53
+ } = functionCallingConfig;
54
+
55
+ payload.toolConfig = {
56
+ ...toolConfig,
57
+ functionCallingConfig: restFunctionCallingConfig,
58
+ };
59
+
60
+ return JSON.stringify(payload);
61
+ } catch {
62
+ return body;
63
+ }
64
+ }
@@ -0,0 +1,202 @@
1
+ import { createAnthropic } from "@ai-sdk/anthropic";
2
+ import { createVertex } from "@ai-sdk/google-vertex";
3
+ import { createOpenAI } from "@ai-sdk/openai";
4
+ import type { LanguageModelV3 } from "@ai-sdk/provider";
5
+ import { API_ENDPOINT } from "@/lib/constants";
6
+ import { createGoogleProxyFetch } from "@/lib/google-proxy-fetch";
7
+
8
+ export type SpstProviderOptions = {
9
+ projectId: string;
10
+ email: string;
11
+ callerApp: string;
12
+ };
13
+
14
+ export type CreateSpstProviderParams = {
15
+ apiKey?: string;
16
+ baseUrl?: string;
17
+ callerApp: string;
18
+ };
19
+
20
+ type SpstProviderConfig = Pick<CreateSpstProviderParams, "apiKey" | "baseUrl">;
21
+ type SpstProviderSet = {
22
+ openai: ReturnType<typeof createOpenAI>;
23
+ google: ReturnType<typeof createVertex>;
24
+ anthropic: ReturnType<typeof createAnthropic>;
25
+ };
26
+ type ParsedSpstModelId =
27
+ | {
28
+ provider: "openai";
29
+ proxyModelId: string;
30
+ }
31
+ | {
32
+ provider: "google";
33
+ proxyModelId: string;
34
+ }
35
+ | {
36
+ provider: "anthropic";
37
+ proxyModelId: string;
38
+ };
39
+
40
+ const spstProviders = createSpstProviders({});
41
+
42
+ export function spst(modelId: string): LanguageModelV3 {
43
+ return createSpstLanguageModel(modelId, spstProviders);
44
+ }
45
+
46
+ export function createSpstProvider(
47
+ params: CreateSpstProviderParams,
48
+ ): typeof spst {
49
+ const providers = createSpstProviders(params);
50
+
51
+ return (modelId: string) => {
52
+ return createSpstLanguageModel(modelId, providers, params.callerApp);
53
+ };
54
+ }
55
+
56
+ function createSpstProviders(config: SpstProviderConfig): SpstProviderSet {
57
+ const apiKey = config.apiKey ?? process.env.AI_AGENT_API_KEY;
58
+
59
+ return {
60
+ openai: createOpenAI({
61
+ name: "spst",
62
+ baseURL: getOpenAIProxyBaseUrl(config.baseUrl),
63
+ apiKey,
64
+ }),
65
+ google: createVertex({
66
+ apiKey,
67
+ baseURL: getGoogleProxyBaseUrl(config.baseUrl),
68
+ fetch: createGoogleProxyFetch(apiKey),
69
+ }),
70
+ anthropic: createAnthropic({
71
+ baseURL: getAnthropicProxyBaseUrl(config.baseUrl),
72
+ apiKey,
73
+ }),
74
+ };
75
+ }
76
+
77
+ function getResolvedBaseUrl(baseUrl?: string) {
78
+ const resolvedBaseUrl =
79
+ baseUrl ?? process.env.AI_AGENT_API_ENDPOINT ?? API_ENDPOINT;
80
+ return resolvedBaseUrl.replace(/\/$/, "");
81
+ }
82
+
83
+ function getOpenAIProxyBaseUrl(baseUrl?: string) {
84
+ return `${getResolvedBaseUrl(baseUrl)}/api/proxy/v1/openai`;
85
+ }
86
+
87
+ function getGoogleProxyBaseUrl(baseUrl?: string) {
88
+ return `${getResolvedBaseUrl(baseUrl)}/api/proxy/v1/google`;
89
+ }
90
+
91
+ function getAnthropicProxyBaseUrl(baseUrl?: string) {
92
+ return `${getResolvedBaseUrl(baseUrl)}/api/proxy/v1/anthropic`;
93
+ }
94
+
95
+ function createSpstLanguageModel(
96
+ modelId: string,
97
+ providers: SpstProviderSet,
98
+ defaultCallerApp?: string,
99
+ ) {
100
+ const parsedModelId = parseSpstModelId(modelId);
101
+
102
+ switch (parsedModelId.provider) {
103
+ case "openai":
104
+ return wrapSpstModel(
105
+ providers.openai.responses(parsedModelId.proxyModelId),
106
+ defaultCallerApp,
107
+ );
108
+ case "google":
109
+ return wrapSpstModel(
110
+ providers.google(parsedModelId.proxyModelId),
111
+ defaultCallerApp,
112
+ );
113
+ case "anthropic":
114
+ return wrapSpstModel(
115
+ providers.anthropic(parsedModelId.proxyModelId),
116
+ defaultCallerApp,
117
+ );
118
+ }
119
+ }
120
+
121
+ function parseSpstModelId(modelId: string): ParsedSpstModelId {
122
+ if (modelId.startsWith("openai/")) {
123
+ return {
124
+ provider: "openai",
125
+ proxyModelId: modelId,
126
+ };
127
+ }
128
+
129
+ if (modelId.startsWith("google/")) {
130
+ return {
131
+ provider: "google",
132
+ proxyModelId: modelId.slice("google/".length),
133
+ };
134
+ }
135
+
136
+ if (modelId.startsWith("anthropic/")) {
137
+ return {
138
+ provider: "anthropic",
139
+ proxyModelId: modelId.slice("anthropic/".length),
140
+ };
141
+ }
142
+
143
+ if (modelId.includes("/")) {
144
+ throw new Error(
145
+ `Provider '${modelId.split("/")[0]}' is not supported by spst() yet.`,
146
+ );
147
+ }
148
+
149
+ throw new Error(
150
+ "The spst() helper currently requires an 'openai/<model>', 'google/<model>', or 'anthropic/<model>' model id.",
151
+ );
152
+ }
153
+
154
+ function wrapSpstModel(
155
+ model: LanguageModelV3,
156
+ defaultCallerApp?: string,
157
+ ): LanguageModelV3 {
158
+ return {
159
+ specificationVersion: model.specificationVersion,
160
+ provider: model.provider,
161
+ modelId: model.modelId,
162
+ supportedUrls: model.supportedUrls,
163
+ doGenerate: (options) =>
164
+ model.doGenerate(withSpstHeaders(options, defaultCallerApp)),
165
+ doStream: (options) =>
166
+ model.doStream(withSpstHeaders(options, defaultCallerApp)),
167
+ };
168
+ }
169
+
170
+ function withSpstHeaders(
171
+ options: Parameters<LanguageModelV3["doGenerate"]>[0],
172
+ defaultCallerApp?: string,
173
+ ): Parameters<LanguageModelV3["doGenerate"]>[0] {
174
+ const spstOptions = options.providerOptions?.spst as
175
+ | Partial<SpstProviderOptions>
176
+ | undefined;
177
+
178
+ if (!spstOptions) {
179
+ return options;
180
+ }
181
+
182
+ const callerApp = spstOptions.callerApp ?? defaultCallerApp;
183
+
184
+ if (!spstOptions.projectId || !spstOptions.email || !callerApp) {
185
+ throw new Error(
186
+ "The spst provider requires projectId, email, and callerApp in providerOptions.spst.",
187
+ );
188
+ }
189
+
190
+ const { spst: _spst, ...providerOptions } = options.providerOptions ?? {};
191
+
192
+ return {
193
+ ...options,
194
+ headers: {
195
+ ...options.headers,
196
+ "x-spst-project-id": spstOptions.projectId,
197
+ "x-spst-user-email": spstOptions.email,
198
+ "x-spst-caller-app": callerApp,
199
+ },
200
+ providerOptions,
201
+ };
202
+ }