plugin-paperclip-personal-openia-adapter 0.1.4 → 0.1.6

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 CHANGED
@@ -21,13 +21,14 @@ npm i plugin-paperclip-personal-openia-adapter
21
21
  | `baseUrl` | Base URL of the OpenAI gateway | `https://api.openai.com/v1` | Base endpoint address (e.g., https://example.com/v1) |
22
22
  | `apiKey` | API Key | – | Access key for the gateway (secret field) |
23
23
  | `model` | Model name | `gpt-4o` | Identifier of the model to be used |
24
+ | `promptTemplate` | Prompt template | – | Optional. Supports `{{agent.name}}`, `{{agent.id}}`, `{{context.prompt}}`, `{{context.task}}`, and `{{context}}`. |
24
25
 
25
26
  ## How it works
26
27
 
27
- 1. Paperclip calls the `handleChat` function with the user's messages.
28
- 2. The adapter constructs a **POST** request to `${baseUrl}/chat/completions`.
28
+ 1. Paperclip calls the adapter's `execute()` with the agent and wake context.
29
+ 2. The adapter sends that context to `${baseUrl}/chat/completions` as an OpenAI-compatible chat request.
29
30
  3. The request includes the header `Authorization: Bearer <apiKey>`.
30
- 4. The response returns the content of `choices[0].message.content`.
31
+ 4. The response content is returned as the Paperclip run summary and stdout log.
31
32
  5. In case of error, friendly messages are generated without mentioning specific providers.
32
33
 
33
34
  ---
@@ -49,13 +50,14 @@ npm i plugin-paperclip-personal-openia-adapter
49
50
  | `baseUrl` | Base URL do gateway OpenAI | `https://api.openai.com/v1` | Endereço base do endpoint (ex.: https://example.com/v1) |
50
51
  | `apiKey` | API Key | – | Chave de acesso ao gateway (campo secreto) |
51
52
  | `model` | Nome do Modelo | `gpt-4o` | Identificador do modelo a ser usado |
53
+ | `promptTemplate` | Template do prompt | – | Opcional. Aceita `{{agent.name}}`, `{{agent.id}}`, `{{context.prompt}}`, `{{context.task}}` e `{{context}}`. |
52
54
 
53
55
  ## Como funciona
54
56
 
55
- 1. O Paperclip chama a função `handleChat` passando as mensagens do usuário.
56
- 2. O adapter monta a requisição **POST** para `${baseUrl}/chat/completions`.
57
+ 1. O Paperclip chama `execute()` com o agente e o contexto do heartbeat.
58
+ 2. O adapter envia esse contexto para `${baseUrl}/chat/completions` usando o formato OpenAI-compatible.
57
59
  3. A requisição inclui o cabeçalho `Authorization: Bearer <apiKey>`.
58
- 4. A resposta é retornada como o conteúdo de `choices[0].message.content`.
60
+ 4. O conteúdo da resposta é devolvido como resumo da execução e log stdout do Paperclip.
59
61
  5. Em caso de erro, mensagens amigáveis são geradas sem citar provedores específicos.
60
62
 
61
63
  ---
package/dist/index.d.ts CHANGED
@@ -5,13 +5,16 @@
5
5
  * OpenAI‑compatible endpoint, therefore error messages refer only to the
6
6
  * generic gateway and not to a specific provider such as "9route".
7
7
  */
8
- import { Adapter, ConfigField } from '@paperclip/sdk';
8
+ import type { ServerAdapterModule } from '@paperclipai/adapter-utils/types';
9
9
  /** ------------------------------------------------------------
10
10
  * 1️⃣ CONFIGURATION SCHEMA (UI displayed in Paperclip)
11
11
  * ------------------------------------------------------------ */
12
- export declare const configSchema: ConfigField[];
13
- /** ------------------------------------------------------------
14
- * 3️⃣ EXPORT ADAPTER FUNCTION
15
- * ------------------------------------------------------------ */
16
- export declare function createServerAdapter(): Adapter;
12
+ export declare const type = "openai_compatible_personal";
13
+ export declare const label = "OpenAI-compatible (personal)";
14
+ export declare const models: {
15
+ id: string;
16
+ label: string;
17
+ }[];
18
+ export declare const agentConfigurationDoc = "# OpenAI-compatible (personal)\n\nCalls an OpenAI-compatible chat completions endpoint for each Paperclip run.\n\nConfigure baseUrl, apiKey, and model in the adapter configuration. The adapter sends the Paperclip wake context as the user message and returns the model response as the run summary. This adapter does not provide tools or directly modify Paperclip tasks.";
19
+ export declare function createServerAdapter(): ServerAdapterModule;
17
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EACL,OAAO,EAIP,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB;;kEAEkE;AAClE,eAAO,MAAM,YAAY,EAAE,WAAW,EAyBrC,CAAC;AAyDF;;kEAEkE;AAClE,wBAAgB,mBAAmB,IAAI,OAAO,CAM7C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAMV,mBAAmB,EACpB,MAAM,kCAAkC,CAAC;AAE1C;;kEAEkE;AAClE,eAAO,MAAM,IAAI,+BAA+B,CAAC;AACjD,eAAO,MAAM,KAAK,iCAAiC,CAAC;AACpD,eAAO,MAAM,MAAM;;;GAA+D,CAAC;AACnF,eAAO,MAAM,qBAAqB,qXAAqX,CAAC;AAwLxZ,wBAAgB,mBAAmB,IAAI,mBAAmB,CAEzD"}
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * External Adapter – OpenAI compatible (e.g., 9route)
4
3
  *
@@ -6,99 +5,176 @@
6
5
  * OpenAI‑compatible endpoint, therefore error messages refer only to the
7
6
  * generic gateway and not to a specific provider such as "9route".
8
7
  */
9
- var __importDefault = (this && this.__importDefault) || function (mod) {
10
- return (mod && mod.__esModule) ? mod : { "default": mod };
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.configSchema = void 0;
14
- exports.createServerAdapter = createServerAdapter;
15
- const axios_1 = __importDefault(require("axios"));
8
+ import axios from 'axios';
16
9
  /** ------------------------------------------------------------
17
10
  * 1️⃣ CONFIGURATION SCHEMA (UI displayed in Paperclip)
18
11
  * ------------------------------------------------------------ */
19
- exports.configSchema = [
20
- {
21
- key: 'baseUrl',
22
- type: 'string',
23
- label: 'Base URL do gateway OpenAI',
24
- default: 'https://api.openai.com/v1',
25
- placeholder: 'https://example.com/v1',
26
- description: 'Endereço base do gateway compatível com a API da OpenAI.',
27
- },
28
- {
29
- key: 'apiKey',
30
- type: 'string',
31
- label: 'API Key',
32
- secret: true,
33
- placeholder: 'sk-*****',
34
- description: 'Chave de acesso ao gateway.',
35
- },
36
- {
37
- key: 'model',
38
- type: 'string',
39
- label: 'Nome do Modelo',
40
- default: 'gpt-4o',
41
- placeholder: 'gpt-4o',
42
- description: 'Identificador do modelo a ser usado.',
43
- },
44
- ];
45
- /** ------------------------------------------------------------
46
- * 2️⃣ CHAT REQUEST HANDLER
47
- * ------------------------------------------------------------ */
48
- async function handleChat(config, request) {
49
- // Ensure the base URL does not end with a slash before appending the path
50
- const url = `${config.baseUrl?.replace(/\/+$/, '')}/chat/completions`;
51
- const payload = {
52
- model: config.model ?? 'gpt-4o',
53
- messages: request.messages,
54
- temperature: request.options?.temperature ?? 0.7,
55
- top_p: request.options?.top_p ?? 1,
56
- };
12
+ export const type = 'openai_compatible_personal';
13
+ export const label = 'OpenAI-compatible (personal)';
14
+ export const models = [{ id: 'gpt-4o', label: 'Gateway model (default: gpt-4o)' }];
15
+ export const agentConfigurationDoc = `# OpenAI-compatible (personal)\n\nCalls an OpenAI-compatible chat completions endpoint for each Paperclip run.\n\nConfigure baseUrl, apiKey, and model in the adapter configuration. The adapter sends the Paperclip wake context as the user message and returns the model response as the run summary. This adapter does not provide tools or directly modify Paperclip tasks.`;
16
+ function readConfig(config) {
17
+ return config;
18
+ }
19
+ function getConnection(config) {
20
+ const baseUrl = typeof config.baseUrl === 'string' ? config.baseUrl.trim().replace(/\/+$/, '') : '';
21
+ const apiKey = typeof config.apiKey === 'string' ? config.apiKey.trim() : '';
22
+ const model = typeof config.model === 'string' && config.model.trim() ? config.model.trim() : 'gpt-4o';
23
+ if (!baseUrl)
24
+ throw new Error('Informe a Base URL do gateway OpenAI-compatible.');
25
+ if (!apiKey)
26
+ throw new Error('Informe a API Key do gateway.');
57
27
  try {
58
- const resp = await axios_1.default.post(url, payload, {
59
- headers: {
60
- 'Content-Type': 'application/json',
61
- Authorization: `Bearer ${config.apiKey}`,
62
- },
63
- timeout: 30000,
28
+ new URL(baseUrl);
29
+ }
30
+ catch {
31
+ throw new Error('A Base URL precisa ser uma URL válida, incluindo https:// ou http://.');
32
+ }
33
+ return { baseUrl, apiKey, model };
34
+ }
35
+ function friendlyError(err) {
36
+ if (!axios.isAxiosError(err))
37
+ return err instanceof Error ? err.message : 'Erro desconhecido ao executar o adapter.';
38
+ const axiosErr = err;
39
+ const status = axiosErr.response?.status;
40
+ if (status === 401 || status === 403)
41
+ return `Credenciais inválidas ou sem permissão (${status}).`;
42
+ if (status === 404)
43
+ return 'Endpoint não encontrado (404). Confira a Base URL do gateway.';
44
+ if (status && status >= 500)
45
+ return `Erro interno do gateway (${status}).`;
46
+ return axiosErr.response?.data?.error?.message ?? axiosErr.message ?? 'Erro ao comunicar com o gateway OpenAI-compatible.';
47
+ }
48
+ function getPrompt(ctx) {
49
+ const wakeContext = ctx.context ?? {};
50
+ const prompt = wakeContext.prompt;
51
+ const task = wakeContext.task;
52
+ const taskText = typeof task === 'string' ? task : task ? JSON.stringify(task, null, 2) : '';
53
+ const fallback = typeof prompt === 'string' && prompt.trim()
54
+ ? prompt.trim()
55
+ : taskText || JSON.stringify(wakeContext, null, 2);
56
+ const template = ctx.config.promptTemplate;
57
+ if (typeof template !== 'string' || !template.trim())
58
+ return fallback;
59
+ return template
60
+ .split('{{agent.id}}').join(ctx.agent.id)
61
+ .split('{{agent.name}}').join(ctx.agent.name)
62
+ .split('{{context.prompt}}').join(typeof prompt === 'string' ? prompt : '')
63
+ .split('{{context.task}}').join(taskText)
64
+ .split('{{context}}').join(JSON.stringify(wakeContext, null, 2));
65
+ }
66
+ async function callGateway(config, messages) {
67
+ const { baseUrl, apiKey, model } = getConnection(config);
68
+ const response = await axios.post(`${baseUrl}/chat/completions`, {
69
+ model,
70
+ messages,
71
+ temperature: typeof config.temperature === 'number' ? config.temperature : 0.7,
72
+ top_p: typeof config.top_p === 'number' ? config.top_p : 1,
73
+ }, {
74
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
75
+ timeout: 120000,
76
+ });
77
+ const content = response.data?.choices?.[0]?.message?.content;
78
+ if (typeof content !== 'string' || !content.trim()) {
79
+ throw new Error('Resposta inesperada do gateway: choices[0].message.content está ausente ou vazia.');
80
+ }
81
+ return { content, response: response.data, requestedModel: model };
82
+ }
83
+ async function execute(ctx) {
84
+ const config = readConfig(ctx.config);
85
+ const prompt = getPrompt(ctx);
86
+ const messages = [
87
+ {
88
+ role: 'system',
89
+ content: `Você é o agente ${ctx.agent.name} do Paperclip. Responda ao contexto da execução de forma útil e objetiva.`,
90
+ },
91
+ { role: 'user', content: prompt },
92
+ ];
93
+ try {
94
+ const result = await callGateway(config, messages);
95
+ await ctx.onLog('stdout', `${result.content}\n`);
96
+ return {
97
+ exitCode: 0,
98
+ signal: null,
99
+ timedOut: false,
100
+ summary: result.content,
101
+ model: result.response.model ?? result.requestedModel,
102
+ provider: 'openai-compatible',
103
+ usage: result.response.usage ? {
104
+ inputTokens: result.response.usage.prompt_tokens ?? 0,
105
+ outputTokens: result.response.usage.completion_tokens ?? 0,
106
+ } : undefined,
107
+ };
108
+ }
109
+ catch (err) {
110
+ const message = friendlyError(err);
111
+ await ctx.onLog('stderr', `${message}\n`);
112
+ return { exitCode: 1, signal: null, timedOut: false, errorMessage: message, model: config.model };
113
+ }
114
+ }
115
+ async function testEnvironment(ctx) {
116
+ const config = readConfig(ctx.config);
117
+ const testedAt = new Date().toISOString();
118
+ try {
119
+ const { baseUrl, apiKey, model } = getConnection(config);
120
+ await axios.get(`${baseUrl}/models`, {
121
+ headers: { Authorization: `Bearer ${apiKey}` },
122
+ timeout: 15000,
64
123
  });
65
- const choice = resp.data?.choices?.[0];
66
- if (!choice?.message?.content) {
67
- throw new Error('Resposta inesperada da API OpenAI‑compatible.');
68
- }
69
- return { content: choice.message.content };
124
+ return {
125
+ adapterType: ctx.adapterType,
126
+ status: 'pass',
127
+ checks: [{ level: 'info', code: 'gateway_reachable', message: `Gateway acessível; modelo configurado: ${model}.` }],
128
+ testedAt,
129
+ };
70
130
  }
71
131
  catch (err) {
72
- // -------- Friendly generic error handling --------
73
- if (axios_1.default.isAxiosError(err)) {
74
- const axiosErr = err;
75
- const status = axiosErr.response?.status;
76
- const data = axiosErr.response?.data;
77
- let friendlyMsg = 'Erro ao comunicar com o gateway OpenAI‑compatible.';
78
- if (status === 401)
79
- friendlyMsg = 'Credenciais inválidas (401).';
80
- else if (status === 404)
81
- friendlyMsg = 'Endpoint não encontrado (404).';
82
- else if (status && status >= 500)
83
- friendlyMsg = 'Erro interno do servidor (5xx).';
84
- else if (data && typeof data === 'object' && 'error' in data) {
85
- // OpenAI‑style error payload
86
- friendlyMsg = `Gateway error: ${data.error?.message ?? friendlyMsg}`;
87
- }
88
- throw new Error(friendlyMsg);
89
- }
90
- // Non‑HTTP errors
91
- throw err;
132
+ const message = friendlyError(err);
133
+ return {
134
+ adapterType: ctx.adapterType,
135
+ status: 'fail',
136
+ checks: [{ level: 'error', code: 'gateway_check_failed', message, hint: 'Confira a Base URL, a API Key e se o gateway oferece GET /models.' }],
137
+ testedAt,
138
+ };
92
139
  }
93
140
  }
94
- /** ------------------------------------------------------------
95
- * 3️⃣ EXPORT ADAPTER FUNCTION
96
- * ------------------------------------------------------------ */
97
- function createServerAdapter() {
141
+ function getConfigSchema() {
98
142
  return {
99
- name: 'OpenAI‑compatible Adapter (personal)',
100
- configSchema: exports.configSchema,
101
- handleChat,
143
+ fields: [
144
+ {
145
+ key: 'baseUrl',
146
+ label: 'Base URL do gateway OpenAI',
147
+ type: 'text',
148
+ default: 'https://api.openai.com/v1',
149
+ required: true,
150
+ hint: 'Endereço base do gateway compatível com a API OpenAI, por exemplo https://example.com/v1.',
151
+ },
152
+ {
153
+ key: 'apiKey',
154
+ label: 'API Key',
155
+ type: 'text',
156
+ required: true,
157
+ hint: 'Chave de acesso ao gateway. O valor é tratado como segredo pelo Paperclip.',
158
+ meta: { secret: true },
159
+ },
160
+ {
161
+ key: 'model',
162
+ label: 'Nome do modelo',
163
+ type: 'text',
164
+ default: 'gpt-4o',
165
+ required: true,
166
+ hint: 'Identificador do modelo disponibilizado pelo gateway.',
167
+ },
168
+ {
169
+ key: 'promptTemplate',
170
+ label: 'Template do prompt',
171
+ type: 'textarea',
172
+ hint: 'Opcional. Use {{agent.name}}, {{agent.id}}, {{context.prompt}}, {{context.task}} ou {{context}}.',
173
+ },
174
+ ],
102
175
  };
103
176
  }
177
+ export function createServerAdapter() {
178
+ return { type, models, agentConfigurationDoc, execute, testEnvironment, getConfigSchema };
179
+ }
104
180
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Compatibility re-exports for consumers of this adapter's earlier local type
3
+ * shim. New adapter code should import these types from the Paperclip SDK.
4
+ */
5
+ export type { AdapterConfigSchema, AdapterEnvironmentTestContext, AdapterEnvironmentTestResult, AdapterExecutionContext, AdapterExecutionResult, ServerAdapterModule, } from '@paperclipai/adapter-utils/types';
6
+ //# sourceMappingURL=paperclip-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paperclip-types.d.ts","sourceRoot":"","sources":["../src/paperclip-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,YAAY,EACV,mBAAmB,EACnB,6BAA6B,EAC7B,4BAA4B,EAC5B,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=paperclip-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paperclip-types.js","sourceRoot":"","sources":["../src/paperclip-types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-paperclip-personal-openia-adapter",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "External adapter for Paperclip AI that connects to an OpenAI‑compatible gateway (e.g., 9route).",
5
5
  "keywords": [
6
6
  "paperclip",
@@ -19,8 +19,16 @@
19
19
  "homepage": "https://github.com/GlebsonS4ntos/plugin-paperclip-personal-openia-adapter#readme",
20
20
  "author": "Glebson Santos <glebsonsantos@outlook.pt>",
21
21
  "license": "MIT",
22
+ "type": "module",
22
23
  "main": "dist/index.js",
23
24
  "types": "dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "default": "./dist/index.js"
30
+ }
31
+ },
24
32
  "files": [
25
33
  "dist",
26
34
  "src",
@@ -35,16 +43,16 @@
35
43
  "axios": "^1.7.2"
36
44
  },
37
45
  "peerDependencies": {
38
- "@paperclip/sdk": "^1.0.0"
46
+ "@paperclipai/adapter-utils": "^2026.1023.0"
39
47
  },
40
48
  "peerDependenciesMeta": {
41
- "@paperclip/sdk": {
49
+ "@paperclipai/adapter-utils": {
42
50
  "optional": true
43
51
  }
44
52
  },
45
53
  "devDependencies": {
46
- "@paperclip/sdk": "file:./sdk",
47
- "typescript": "^5.5.4",
48
- "rimraf": "^5.0.5"
54
+ "@paperclipai/adapter-utils": "2026.1023.0",
55
+ "rimraf": "^5.0.5",
56
+ "typescript": "^5.5.4"
49
57
  }
50
58
  }
package/src/index.ts CHANGED
@@ -7,106 +7,205 @@
7
7
  */
8
8
 
9
9
  import axios, { AxiosError } from 'axios';
10
- import {
11
- Adapter,
12
- AdapterConfig,
13
- ChatRequest,
14
- ChatResponse,
15
- ConfigField,
16
- } from '@paperclip/sdk';
10
+ import type {
11
+ AdapterConfigSchema,
12
+ AdapterEnvironmentTestContext,
13
+ AdapterEnvironmentTestResult,
14
+ AdapterExecutionContext,
15
+ AdapterExecutionResult,
16
+ ServerAdapterModule,
17
+ } from '@paperclipai/adapter-utils/types';
17
18
 
18
19
  /** ------------------------------------------------------------
19
20
  * 1️⃣ CONFIGURATION SCHEMA (UI displayed in Paperclip)
20
21
  * ------------------------------------------------------------ */
21
- export const configSchema: ConfigField[] = [
22
- {
23
- key: 'baseUrl',
24
- type: 'string',
25
- label: 'Base URL do gateway OpenAI',
26
- default: 'https://api.openai.com/v1',
27
- placeholder: 'https://example.com/v1',
28
- description: 'Endereço base do gateway compatível com a API da OpenAI.',
29
- },
30
- {
31
- key: 'apiKey',
32
- type: 'string',
33
- label: 'API Key',
34
- secret: true,
35
- placeholder: 'sk-*****',
36
- description: 'Chave de acesso ao gateway.',
37
- },
38
- {
39
- key: 'model',
40
- type: 'string',
41
- label: 'Nome do Modelo',
42
- default: 'gpt-4o',
43
- placeholder: 'gpt-4o',
44
- description: 'Identificador do modelo a ser usado.',
45
- },
46
- ];
22
+ export const type = 'openai_compatible_personal';
23
+ export const label = 'OpenAI-compatible (personal)';
24
+ export const models = [{ id: 'gpt-4o', label: 'Gateway model (default: gpt-4o)' }];
25
+ export const agentConfigurationDoc = `# OpenAI-compatible (personal)\n\nCalls an OpenAI-compatible chat completions endpoint for each Paperclip run.\n\nConfigure baseUrl, apiKey, and model in the adapter configuration. The adapter sends the Paperclip wake context as the user message and returns the model response as the run summary. This adapter does not provide tools or directly modify Paperclip tasks.`;
47
26
 
48
- /** ------------------------------------------------------------
49
- * 2️⃣ CHAT REQUEST HANDLER
50
- * ------------------------------------------------------------ */
51
- async function handleChat(
52
- config: AdapterConfig,
53
- request: ChatRequest
54
- ): Promise<ChatResponse> {
55
- // Ensure the base URL does not end with a slash before appending the path
56
- const url = `${config.baseUrl?.replace(/\/+$/, '')}/chat/completions`;
57
-
58
- const payload = {
59
- model: config.model ?? 'gpt-4o',
60
- messages: request.messages,
61
- temperature: request.options?.temperature ?? 0.7,
62
- top_p: request.options?.top_p ?? 1,
63
- };
27
+ interface GatewayConfig {
28
+ baseUrl?: unknown;
29
+ apiKey?: unknown;
30
+ model?: unknown;
31
+ temperature?: unknown;
32
+ top_p?: unknown;
33
+ promptTemplate?: unknown;
34
+ }
35
+
36
+ interface GatewayChatResponse {
37
+ choices?: Array<{ message?: { content?: unknown } }>;
38
+ model?: string;
39
+ usage?: { prompt_tokens?: number; completion_tokens?: number };
40
+ }
64
41
 
42
+ function readConfig(config: Record<string, unknown>): GatewayConfig {
43
+ return config as GatewayConfig;
44
+ }
45
+
46
+ function getConnection(config: GatewayConfig): { baseUrl: string; apiKey: string; model: string } {
47
+ const baseUrl = typeof config.baseUrl === 'string' ? config.baseUrl.trim().replace(/\/+$/, '') : '';
48
+ const apiKey = typeof config.apiKey === 'string' ? config.apiKey.trim() : '';
49
+ const model = typeof config.model === 'string' && config.model.trim() ? config.model.trim() : 'gpt-4o';
50
+
51
+ if (!baseUrl) throw new Error('Informe a Base URL do gateway OpenAI-compatible.');
52
+ if (!apiKey) throw new Error('Informe a API Key do gateway.');
65
53
  try {
66
- const resp = await axios.post(url, payload, {
67
- headers: {
68
- 'Content-Type': 'application/json',
69
- Authorization: `Bearer ${config.apiKey}`,
70
- },
71
- timeout: 30_000,
72
- });
54
+ new URL(baseUrl);
55
+ } catch {
56
+ throw new Error('A Base URL precisa ser uma URL válida, incluindo https:// ou http://.');
57
+ }
58
+
59
+ return { baseUrl, apiKey, model };
60
+ }
61
+
62
+ function friendlyError(err: unknown): string {
63
+ if (!axios.isAxiosError(err)) return err instanceof Error ? err.message : 'Erro desconhecido ao executar o adapter.';
64
+
65
+ const axiosErr = err as AxiosError<{ error?: { message?: string } }>;
66
+ const status = axiosErr.response?.status;
67
+ if (status === 401 || status === 403) return `Credenciais inválidas ou sem permissão (${status}).`;
68
+ if (status === 404) return 'Endpoint não encontrado (404). Confira a Base URL do gateway.';
69
+ if (status && status >= 500) return `Erro interno do gateway (${status}).`;
70
+ return axiosErr.response?.data?.error?.message ?? axiosErr.message ?? 'Erro ao comunicar com o gateway OpenAI-compatible.';
71
+ }
72
+
73
+ function getPrompt(ctx: AdapterExecutionContext): string {
74
+ const wakeContext = ctx.context ?? {};
75
+ const prompt = wakeContext.prompt;
76
+ const task = wakeContext.task;
77
+ const taskText = typeof task === 'string' ? task : task ? JSON.stringify(task, null, 2) : '';
78
+ const fallback = typeof prompt === 'string' && prompt.trim()
79
+ ? prompt.trim()
80
+ : taskText || JSON.stringify(wakeContext, null, 2);
81
+
82
+ const template = ctx.config.promptTemplate;
83
+ if (typeof template !== 'string' || !template.trim()) return fallback;
84
+
85
+ return template
86
+ .split('{{agent.id}}').join(ctx.agent.id)
87
+ .split('{{agent.name}}').join(ctx.agent.name)
88
+ .split('{{context.prompt}}').join(typeof prompt === 'string' ? prompt : '')
89
+ .split('{{context.task}}').join(taskText)
90
+ .split('{{context}}').join(JSON.stringify(wakeContext, null, 2));
91
+ }
73
92
 
74
- const choice = resp.data?.choices?.[0];
75
- if (!choice?.message?.content) {
76
- throw new Error('Resposta inesperada da API OpenAI‑compatible.');
77
- }
93
+ async function callGateway(config: GatewayConfig, messages: Array<{ role: string; content: string }>) {
94
+ const { baseUrl, apiKey, model } = getConnection(config);
95
+ const response = await axios.post<GatewayChatResponse>(`${baseUrl}/chat/completions`, {
96
+ model,
97
+ messages,
98
+ temperature: typeof config.temperature === 'number' ? config.temperature : 0.7,
99
+ top_p: typeof config.top_p === 'number' ? config.top_p : 1,
100
+ }, {
101
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
102
+ timeout: 120_000,
103
+ });
78
104
 
79
- return { content: choice.message.content } as ChatResponse;
105
+ const content = response.data?.choices?.[0]?.message?.content;
106
+ if (typeof content !== 'string' || !content.trim()) {
107
+ throw new Error('Resposta inesperada do gateway: choices[0].message.content está ausente ou vazia.');
108
+ }
109
+
110
+ return { content, response: response.data, requestedModel: model };
111
+ }
112
+
113
+ async function execute(ctx: AdapterExecutionContext): Promise<AdapterExecutionResult> {
114
+ const config = readConfig(ctx.config);
115
+ const prompt = getPrompt(ctx);
116
+ const messages = [
117
+ {
118
+ role: 'system',
119
+ content: `Você é o agente ${ctx.agent.name} do Paperclip. Responda ao contexto da execução de forma útil e objetiva.`,
120
+ },
121
+ { role: 'user', content: prompt },
122
+ ];
123
+
124
+ try {
125
+ const result = await callGateway(config, messages);
126
+ await ctx.onLog('stdout', `${result.content}\n`);
127
+ return {
128
+ exitCode: 0,
129
+ signal: null,
130
+ timedOut: false,
131
+ summary: result.content,
132
+ model: result.response.model ?? result.requestedModel,
133
+ provider: 'openai-compatible',
134
+ usage: result.response.usage ? {
135
+ inputTokens: result.response.usage.prompt_tokens ?? 0,
136
+ outputTokens: result.response.usage.completion_tokens ?? 0,
137
+ } : undefined,
138
+ };
80
139
  } catch (err) {
81
- // -------- Friendly generic error handling --------
82
- if (axios.isAxiosError(err)) {
83
- const axiosErr = err as AxiosError;
84
- const status = axiosErr.response?.status;
85
- const data = axiosErr.response?.data;
86
-
87
- let friendlyMsg = 'Erro ao comunicar com o gateway OpenAI‑compatible.';
88
- if (status === 401) friendlyMsg = 'Credenciais inválidas (401).';
89
- else if (status === 404) friendlyMsg = 'Endpoint não encontrado (404).';
90
- else if (status && status >= 500) friendlyMsg = 'Erro interno do servidor (5xx).';
91
- else if (data && typeof data === 'object' && 'error' in data) {
92
- // OpenAI‑style error payload
93
- friendlyMsg = `Gateway error: ${(data as any).error?.message ?? friendlyMsg}`;
94
- }
95
-
96
- throw new Error(friendlyMsg);
97
- }
98
- // Non‑HTTP errors
99
- throw err;
140
+ const message = friendlyError(err);
141
+ await ctx.onLog('stderr', `${message}\n`);
142
+ return { exitCode: 1, signal: null, timedOut: false, errorMessage: message, model: config.model as string | undefined };
100
143
  }
101
144
  }
102
145
 
103
- /** ------------------------------------------------------------
104
- * 3️⃣ EXPORT ADAPTER FUNCTION
105
- * ------------------------------------------------------------ */
106
- export function createServerAdapter(): Adapter {
146
+ async function testEnvironment(ctx: AdapterEnvironmentTestContext): Promise<AdapterEnvironmentTestResult> {
147
+ const config = readConfig(ctx.config);
148
+ const testedAt = new Date().toISOString();
149
+ try {
150
+ const { baseUrl, apiKey, model } = getConnection(config);
151
+ await axios.get(`${baseUrl}/models`, {
152
+ headers: { Authorization: `Bearer ${apiKey}` },
153
+ timeout: 15_000,
154
+ });
155
+ return {
156
+ adapterType: ctx.adapterType,
157
+ status: 'pass',
158
+ checks: [{ level: 'info', code: 'gateway_reachable', message: `Gateway acessível; modelo configurado: ${model}.` }],
159
+ testedAt,
160
+ };
161
+ } catch (err) {
162
+ const message = friendlyError(err);
163
+ return {
164
+ adapterType: ctx.adapterType,
165
+ status: 'fail',
166
+ checks: [{ level: 'error', code: 'gateway_check_failed', message, hint: 'Confira a Base URL, a API Key e se o gateway oferece GET /models.' }],
167
+ testedAt,
168
+ };
169
+ }
170
+ }
171
+
172
+ function getConfigSchema(): AdapterConfigSchema {
107
173
  return {
108
- name: 'OpenAI‑compatible Adapter (personal)',
109
- configSchema,
110
- handleChat,
174
+ fields: [
175
+ {
176
+ key: 'baseUrl',
177
+ label: 'Base URL do gateway OpenAI',
178
+ type: 'text',
179
+ default: 'https://api.openai.com/v1',
180
+ required: true,
181
+ hint: 'Endereço base do gateway compatível com a API OpenAI, por exemplo https://example.com/v1.',
182
+ },
183
+ {
184
+ key: 'apiKey',
185
+ label: 'API Key',
186
+ type: 'text',
187
+ required: true,
188
+ hint: 'Chave de acesso ao gateway. O valor é tratado como segredo pelo Paperclip.',
189
+ meta: { secret: true },
190
+ },
191
+ {
192
+ key: 'model',
193
+ label: 'Nome do modelo',
194
+ type: 'text',
195
+ default: 'gpt-4o',
196
+ required: true,
197
+ hint: 'Identificador do modelo disponibilizado pelo gateway.',
198
+ },
199
+ {
200
+ key: 'promptTemplate',
201
+ label: 'Template do prompt',
202
+ type: 'textarea',
203
+ hint: 'Opcional. Use {{agent.name}}, {{agent.id}}, {{context.prompt}}, {{context.task}} ou {{context}}.',
204
+ },
205
+ ],
111
206
  };
112
- }
207
+ }
208
+
209
+ export function createServerAdapter(): ServerAdapterModule {
210
+ return { type, models, agentConfigurationDoc, execute, testEnvironment, getConfigSchema };
211
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Compatibility re-exports for consumers of this adapter's earlier local type
3
+ * shim. New adapter code should import these types from the Paperclip SDK.
4
+ */
5
+ export type {
6
+ AdapterConfigSchema,
7
+ AdapterEnvironmentTestContext,
8
+ AdapterEnvironmentTestResult,
9
+ AdapterExecutionContext,
10
+ AdapterExecutionResult,
11
+ ServerAdapterModule,
12
+ } from '@paperclipai/adapter-utils/types';