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 +8 -6
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +160 -84
- package/dist/index.js.map +1 -1
- package/dist/paperclip-types.d.ts +6 -0
- package/dist/paperclip-types.d.ts.map +1 -0
- package/dist/paperclip-types.js +2 -0
- package/dist/paperclip-types.js.map +1 -0
- package/package.json +14 -6
- package/src/index.ts +187 -88
- package/src/paperclip-types.ts +12 -0
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 `
|
|
28
|
-
2. The adapter
|
|
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
|
|
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
|
|
56
|
-
2. O adapter
|
|
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.
|
|
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 {
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,
|
|
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
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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":"
|
|
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 @@
|
|
|
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.
|
|
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
|
-
"@
|
|
46
|
+
"@paperclipai/adapter-utils": "^2026.1023.0"
|
|
39
47
|
},
|
|
40
48
|
"peerDependenciesMeta": {
|
|
41
|
-
"@
|
|
49
|
+
"@paperclipai/adapter-utils": {
|
|
42
50
|
"optional": true
|
|
43
51
|
}
|
|
44
52
|
},
|
|
45
53
|
"devDependencies": {
|
|
46
|
-
"@
|
|
47
|
-
"
|
|
48
|
-
"
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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';
|