plugin-paperclip-personal-openia-adapter 0.1.3 → 0.1.5
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 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +134 -61
- package/dist/index.js.map +1 -1
- package/dist/paperclip-types.d.ts +66 -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 +9 -10
- package/src/index.ts +159 -63
- package/src/paperclip-types.ts +56 -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,17 @@
|
|
|
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 { ConfigField, ServerAdapterModule } from './paperclip-types.js';
|
|
9
9
|
/** ------------------------------------------------------------
|
|
10
10
|
* 1️⃣ CONFIGURATION SCHEMA (UI displayed in Paperclip)
|
|
11
11
|
* ------------------------------------------------------------ */
|
|
12
12
|
export declare const configSchema: ConfigField[];
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
export declare const type = "openai_compatible_personal";
|
|
14
|
+
export declare const label = "OpenAI-compatible (personal)";
|
|
15
|
+
export declare const models: {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
}[];
|
|
19
|
+
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.";
|
|
20
|
+
export declare function createServerAdapter(): ServerAdapterModule;
|
|
17
21
|
//# 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,EAKV,WAAW,EACX,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAE9B;;kEAEkE;AAClE,eAAO,MAAM,YAAY,EAAE,WAAW,EAgCrC,CAAC;AAEF,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;AAmJxZ,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,17 +5,11 @@
|
|
|
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
|
-
|
|
12
|
+
export const configSchema = [
|
|
20
13
|
{
|
|
21
14
|
key: 'baseUrl',
|
|
22
15
|
type: 'string',
|
|
@@ -41,64 +34,144 @@ exports.configSchema = [
|
|
|
41
34
|
placeholder: 'gpt-4o',
|
|
42
35
|
description: 'Identificador do modelo a ser usado.',
|
|
43
36
|
},
|
|
37
|
+
{
|
|
38
|
+
key: 'promptTemplate',
|
|
39
|
+
type: 'string',
|
|
40
|
+
label: 'Template do prompt',
|
|
41
|
+
placeholder: 'Atenda esta solicitação: {{context.prompt}}',
|
|
42
|
+
description: 'Opcional. Use {{agent.name}}, {{agent.id}}, {{context.prompt}}, {{context.task}} ou {{context}}.',
|
|
43
|
+
},
|
|
44
44
|
];
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
45
|
+
export const type = 'openai_compatible_personal';
|
|
46
|
+
export const label = 'OpenAI-compatible (personal)';
|
|
47
|
+
export const models = [{ id: 'gpt-4o', label: 'Gateway model (default: gpt-4o)' }];
|
|
48
|
+
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.`;
|
|
49
|
+
function readConfig(config) {
|
|
50
|
+
return config;
|
|
51
|
+
}
|
|
52
|
+
function getConnection(config) {
|
|
53
|
+
const baseUrl = typeof config.baseUrl === 'string' ? config.baseUrl.trim().replace(/\/+$/, '') : '';
|
|
54
|
+
const apiKey = typeof config.apiKey === 'string' ? config.apiKey.trim() : '';
|
|
55
|
+
const model = typeof config.model === 'string' && config.model.trim() ? config.model.trim() : 'gpt-4o';
|
|
56
|
+
if (!baseUrl)
|
|
57
|
+
throw new Error('Informe a Base URL do gateway OpenAI-compatible.');
|
|
58
|
+
if (!apiKey)
|
|
59
|
+
throw new Error('Informe a API Key do gateway.');
|
|
57
60
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
new URL(baseUrl);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
throw new Error('A Base URL precisa ser uma URL válida, incluindo https:// ou http://.');
|
|
65
|
+
}
|
|
66
|
+
return { baseUrl, apiKey, model };
|
|
67
|
+
}
|
|
68
|
+
function friendlyError(err) {
|
|
69
|
+
if (!axios.isAxiosError(err))
|
|
70
|
+
return err instanceof Error ? err.message : 'Erro desconhecido ao executar o adapter.';
|
|
71
|
+
const axiosErr = err;
|
|
72
|
+
const status = axiosErr.response?.status;
|
|
73
|
+
if (status === 401 || status === 403)
|
|
74
|
+
return `Credenciais inválidas ou sem permissão (${status}).`;
|
|
75
|
+
if (status === 404)
|
|
76
|
+
return 'Endpoint não encontrado (404). Confira a Base URL do gateway.';
|
|
77
|
+
if (status && status >= 500)
|
|
78
|
+
return `Erro interno do gateway (${status}).`;
|
|
79
|
+
return axiosErr.response?.data?.error?.message ?? axiosErr.message ?? 'Erro ao comunicar com o gateway OpenAI-compatible.';
|
|
80
|
+
}
|
|
81
|
+
function getPrompt(ctx) {
|
|
82
|
+
const wakeContext = ctx.context ?? {};
|
|
83
|
+
const prompt = wakeContext.prompt;
|
|
84
|
+
const task = wakeContext.task;
|
|
85
|
+
const taskText = typeof task === 'string' ? task : task ? JSON.stringify(task, null, 2) : '';
|
|
86
|
+
const fallback = typeof prompt === 'string' && prompt.trim()
|
|
87
|
+
? prompt.trim()
|
|
88
|
+
: taskText || JSON.stringify(wakeContext, null, 2);
|
|
89
|
+
const template = ctx.config.promptTemplate;
|
|
90
|
+
if (typeof template !== 'string' || !template.trim())
|
|
91
|
+
return fallback;
|
|
92
|
+
return template
|
|
93
|
+
.split('{{agent.id}}').join(ctx.agent.id)
|
|
94
|
+
.split('{{agent.name}}').join(ctx.agent.name)
|
|
95
|
+
.split('{{context.prompt}}').join(typeof prompt === 'string' ? prompt : '')
|
|
96
|
+
.split('{{context.task}}').join(taskText)
|
|
97
|
+
.split('{{context}}').join(JSON.stringify(wakeContext, null, 2));
|
|
98
|
+
}
|
|
99
|
+
async function callGateway(config, messages) {
|
|
100
|
+
const { baseUrl, apiKey, model } = getConnection(config);
|
|
101
|
+
const response = await axios.post(`${baseUrl}/chat/completions`, {
|
|
102
|
+
model,
|
|
103
|
+
messages,
|
|
104
|
+
temperature: typeof config.temperature === 'number' ? config.temperature : 0.7,
|
|
105
|
+
top_p: typeof config.top_p === 'number' ? config.top_p : 1,
|
|
106
|
+
}, {
|
|
107
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
|
108
|
+
timeout: 120000,
|
|
109
|
+
});
|
|
110
|
+
const content = response.data?.choices?.[0]?.message?.content;
|
|
111
|
+
if (typeof content !== 'string' || !content.trim()) {
|
|
112
|
+
throw new Error('Resposta inesperada do gateway: choices[0].message.content está ausente ou vazia.');
|
|
113
|
+
}
|
|
114
|
+
return { content, response: response.data, requestedModel: model };
|
|
115
|
+
}
|
|
116
|
+
async function execute(ctx) {
|
|
117
|
+
const config = readConfig(ctx.config);
|
|
118
|
+
const prompt = getPrompt(ctx);
|
|
119
|
+
const messages = [
|
|
120
|
+
{
|
|
121
|
+
role: 'system',
|
|
122
|
+
content: `Você é o agente ${ctx.agent.name} do Paperclip. Responda ao contexto da execução de forma útil e objetiva.`,
|
|
123
|
+
},
|
|
124
|
+
{ role: 'user', content: prompt },
|
|
125
|
+
];
|
|
126
|
+
try {
|
|
127
|
+
const result = await callGateway(config, messages);
|
|
128
|
+
await ctx.onLog('stdout', `${result.content}\n`);
|
|
129
|
+
return {
|
|
130
|
+
exitCode: 0,
|
|
131
|
+
signal: null,
|
|
132
|
+
timedOut: false,
|
|
133
|
+
summary: result.content,
|
|
134
|
+
model: result.response.model ?? result.requestedModel,
|
|
135
|
+
provider: 'openai-compatible',
|
|
136
|
+
usage: result.response.usage ? {
|
|
137
|
+
inputTokens: result.response.usage.prompt_tokens ?? 0,
|
|
138
|
+
outputTokens: result.response.usage.completion_tokens ?? 0,
|
|
139
|
+
} : undefined,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
const message = friendlyError(err);
|
|
144
|
+
await ctx.onLog('stderr', `${message}\n`);
|
|
145
|
+
return { exitCode: 1, signal: null, timedOut: false, errorMessage: message, model: config.model };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function testEnvironment(ctx) {
|
|
149
|
+
const config = readConfig(ctx.config);
|
|
150
|
+
const testedAt = new Date().toISOString();
|
|
151
|
+
try {
|
|
152
|
+
const { baseUrl, apiKey, model } = getConnection(config);
|
|
153
|
+
await axios.get(`${baseUrl}/models`, {
|
|
154
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
155
|
+
timeout: 15000,
|
|
64
156
|
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
157
|
+
return {
|
|
158
|
+
adapterType: ctx.adapterType,
|
|
159
|
+
status: 'pass',
|
|
160
|
+
checks: [{ level: 'info', code: 'gateway_reachable', message: `Gateway acessível; modelo configurado: ${model}.` }],
|
|
161
|
+
testedAt,
|
|
162
|
+
};
|
|
70
163
|
}
|
|
71
164
|
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;
|
|
165
|
+
const message = friendlyError(err);
|
|
166
|
+
return {
|
|
167
|
+
adapterType: ctx.adapterType,
|
|
168
|
+
status: 'fail',
|
|
169
|
+
checks: [{ level: 'error', code: 'gateway_check_failed', message, hint: 'Confira a Base URL, a API Key e se o gateway oferece GET /models.' }],
|
|
170
|
+
testedAt,
|
|
171
|
+
};
|
|
92
172
|
}
|
|
93
173
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
* ------------------------------------------------------------ */
|
|
97
|
-
function createServerAdapter() {
|
|
98
|
-
return {
|
|
99
|
-
name: 'OpenAI‑compatible Adapter (personal)',
|
|
100
|
-
configSchema: exports.configSchema,
|
|
101
|
-
handleChat,
|
|
102
|
-
};
|
|
174
|
+
export function createServerAdapter() {
|
|
175
|
+
return { type, label, models, agentConfigurationDoc, configSchema, execute, testEnvironment };
|
|
103
176
|
}
|
|
104
177
|
//# 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,66 @@
|
|
|
1
|
+
export interface ConfigField {
|
|
2
|
+
key: string;
|
|
3
|
+
type: string;
|
|
4
|
+
label: string;
|
|
5
|
+
default?: unknown;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
secret?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface AdapterExecutionContext {
|
|
11
|
+
runId: string;
|
|
12
|
+
agent: {
|
|
13
|
+
id: string;
|
|
14
|
+
companyId: string;
|
|
15
|
+
name: string;
|
|
16
|
+
adapterConfig: unknown;
|
|
17
|
+
};
|
|
18
|
+
runtime: {
|
|
19
|
+
sessionId: string | null;
|
|
20
|
+
sessionParams: Record<string, unknown> | null;
|
|
21
|
+
};
|
|
22
|
+
config: Record<string, unknown>;
|
|
23
|
+
context: Record<string, any>;
|
|
24
|
+
onLog: (stream: 'stdout' | 'stderr', chunk: string) => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export interface AdapterExecutionResult {
|
|
27
|
+
exitCode: number | null;
|
|
28
|
+
signal: string | null;
|
|
29
|
+
timedOut: boolean;
|
|
30
|
+
errorMessage?: string | null;
|
|
31
|
+
summary?: string | null;
|
|
32
|
+
usage?: {
|
|
33
|
+
inputTokens: number;
|
|
34
|
+
outputTokens: number;
|
|
35
|
+
};
|
|
36
|
+
provider?: string | null;
|
|
37
|
+
model?: string | null;
|
|
38
|
+
}
|
|
39
|
+
export interface AdapterEnvironmentTestContext {
|
|
40
|
+
adapterType: string;
|
|
41
|
+
config: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
export interface AdapterEnvironmentTestResult {
|
|
44
|
+
adapterType: string;
|
|
45
|
+
status: 'pass' | 'warn' | 'fail';
|
|
46
|
+
checks: Array<{
|
|
47
|
+
level: 'info' | 'warn' | 'error';
|
|
48
|
+
code: string;
|
|
49
|
+
message: string;
|
|
50
|
+
hint?: string;
|
|
51
|
+
}>;
|
|
52
|
+
testedAt: string;
|
|
53
|
+
}
|
|
54
|
+
export interface ServerAdapterModule {
|
|
55
|
+
type: string;
|
|
56
|
+
label?: string;
|
|
57
|
+
models: Array<{
|
|
58
|
+
id: string;
|
|
59
|
+
label: string;
|
|
60
|
+
}>;
|
|
61
|
+
agentConfigurationDoc: string;
|
|
62
|
+
configSchema: ConfigField[];
|
|
63
|
+
execute(ctx: AdapterExecutionContext): Promise<AdapterExecutionResult>;
|
|
64
|
+
testEnvironment(ctx: AdapterEnvironmentTestContext): Promise<AdapterEnvironmentTestResult>;
|
|
65
|
+
}
|
|
66
|
+
//# 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,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/E,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC;IACrF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,6BAA6B;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,MAAM,EAAE,KAAK,CAAC;QACZ,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;QACjC,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7C,qBAAqB,EAAE,MAAM,CAAC;IAC9B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,OAAO,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACvE,eAAe,CAAC,GAAG,EAAE,6BAA6B,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAC5F"}
|
|
@@ -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.5",
|
|
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",
|
|
@@ -34,16 +42,7 @@
|
|
|
34
42
|
"dependencies": {
|
|
35
43
|
"axios": "^1.7.2"
|
|
36
44
|
},
|
|
37
|
-
"peerDependencies": {
|
|
38
|
-
"@paperclip/sdk": "^1.0.0"
|
|
39
|
-
},
|
|
40
|
-
"peerDependenciesMeta": {
|
|
41
|
-
"@paperclip/sdk": {
|
|
42
|
-
"optional": true
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@paperclip/sdk": "file:./sdk",
|
|
47
46
|
"typescript": "^5.5.4",
|
|
48
47
|
"rimraf": "^5.0.5"
|
|
49
48
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import axios, { AxiosError } from 'axios';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
import type {
|
|
11
|
+
AdapterEnvironmentTestContext,
|
|
12
|
+
AdapterEnvironmentTestResult,
|
|
13
|
+
AdapterExecutionContext,
|
|
14
|
+
AdapterExecutionResult,
|
|
15
15
|
ConfigField,
|
|
16
|
-
|
|
16
|
+
ServerAdapterModule,
|
|
17
|
+
} from './paperclip-types.js';
|
|
17
18
|
|
|
18
19
|
/** ------------------------------------------------------------
|
|
19
20
|
* 1️⃣ CONFIGURATION SCHEMA (UI displayed in Paperclip)
|
|
@@ -43,70 +44,165 @@ export const configSchema: ConfigField[] = [
|
|
|
43
44
|
placeholder: 'gpt-4o',
|
|
44
45
|
description: 'Identificador do modelo a ser usado.',
|
|
45
46
|
},
|
|
47
|
+
{
|
|
48
|
+
key: 'promptTemplate',
|
|
49
|
+
type: 'string',
|
|
50
|
+
label: 'Template do prompt',
|
|
51
|
+
placeholder: 'Atenda esta solicitação: {{context.prompt}}',
|
|
52
|
+
description: 'Opcional. Use {{agent.name}}, {{agent.id}}, {{context.prompt}}, {{context.task}} ou {{context}}.',
|
|
53
|
+
},
|
|
46
54
|
];
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
};
|
|
56
|
+
export const type = 'openai_compatible_personal';
|
|
57
|
+
export const label = 'OpenAI-compatible (personal)';
|
|
58
|
+
export const models = [{ id: 'gpt-4o', label: 'Gateway model (default: gpt-4o)' }];
|
|
59
|
+
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.`;
|
|
64
60
|
|
|
61
|
+
interface GatewayConfig {
|
|
62
|
+
baseUrl?: unknown;
|
|
63
|
+
apiKey?: unknown;
|
|
64
|
+
model?: unknown;
|
|
65
|
+
temperature?: unknown;
|
|
66
|
+
top_p?: unknown;
|
|
67
|
+
promptTemplate?: unknown;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface GatewayChatResponse {
|
|
71
|
+
choices?: Array<{ message?: { content?: unknown } }>;
|
|
72
|
+
model?: string;
|
|
73
|
+
usage?: { prompt_tokens?: number; completion_tokens?: number };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readConfig(config: Record<string, unknown>): GatewayConfig {
|
|
77
|
+
return config as GatewayConfig;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getConnection(config: GatewayConfig): { baseUrl: string; apiKey: string; model: string } {
|
|
81
|
+
const baseUrl = typeof config.baseUrl === 'string' ? config.baseUrl.trim().replace(/\/+$/, '') : '';
|
|
82
|
+
const apiKey = typeof config.apiKey === 'string' ? config.apiKey.trim() : '';
|
|
83
|
+
const model = typeof config.model === 'string' && config.model.trim() ? config.model.trim() : 'gpt-4o';
|
|
84
|
+
|
|
85
|
+
if (!baseUrl) throw new Error('Informe a Base URL do gateway OpenAI-compatible.');
|
|
86
|
+
if (!apiKey) throw new Error('Informe a API Key do gateway.');
|
|
65
87
|
try {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
new URL(baseUrl);
|
|
89
|
+
} catch {
|
|
90
|
+
throw new Error('A Base URL precisa ser uma URL válida, incluindo https:// ou http://.');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { baseUrl, apiKey, model };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function friendlyError(err: unknown): string {
|
|
97
|
+
if (!axios.isAxiosError(err)) return err instanceof Error ? err.message : 'Erro desconhecido ao executar o adapter.';
|
|
98
|
+
|
|
99
|
+
const axiosErr = err as AxiosError<{ error?: { message?: string } }>;
|
|
100
|
+
const status = axiosErr.response?.status;
|
|
101
|
+
if (status === 401 || status === 403) return `Credenciais inválidas ou sem permissão (${status}).`;
|
|
102
|
+
if (status === 404) return 'Endpoint não encontrado (404). Confira a Base URL do gateway.';
|
|
103
|
+
if (status && status >= 500) return `Erro interno do gateway (${status}).`;
|
|
104
|
+
return axiosErr.response?.data?.error?.message ?? axiosErr.message ?? 'Erro ao comunicar com o gateway OpenAI-compatible.';
|
|
105
|
+
}
|
|
73
106
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
107
|
+
function getPrompt(ctx: AdapterExecutionContext): string {
|
|
108
|
+
const wakeContext = ctx.context ?? {};
|
|
109
|
+
const prompt = wakeContext.prompt;
|
|
110
|
+
const task = wakeContext.task;
|
|
111
|
+
const taskText = typeof task === 'string' ? task : task ? JSON.stringify(task, null, 2) : '';
|
|
112
|
+
const fallback = typeof prompt === 'string' && prompt.trim()
|
|
113
|
+
? prompt.trim()
|
|
114
|
+
: taskText || JSON.stringify(wakeContext, null, 2);
|
|
78
115
|
|
|
79
|
-
|
|
116
|
+
const template = ctx.config.promptTemplate;
|
|
117
|
+
if (typeof template !== 'string' || !template.trim()) return fallback;
|
|
118
|
+
|
|
119
|
+
return template
|
|
120
|
+
.split('{{agent.id}}').join(ctx.agent.id)
|
|
121
|
+
.split('{{agent.name}}').join(ctx.agent.name)
|
|
122
|
+
.split('{{context.prompt}}').join(typeof prompt === 'string' ? prompt : '')
|
|
123
|
+
.split('{{context.task}}').join(taskText)
|
|
124
|
+
.split('{{context}}').join(JSON.stringify(wakeContext, null, 2));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function callGateway(config: GatewayConfig, messages: Array<{ role: string; content: string }>) {
|
|
128
|
+
const { baseUrl, apiKey, model } = getConnection(config);
|
|
129
|
+
const response = await axios.post<GatewayChatResponse>(`${baseUrl}/chat/completions`, {
|
|
130
|
+
model,
|
|
131
|
+
messages,
|
|
132
|
+
temperature: typeof config.temperature === 'number' ? config.temperature : 0.7,
|
|
133
|
+
top_p: typeof config.top_p === 'number' ? config.top_p : 1,
|
|
134
|
+
}, {
|
|
135
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
|
136
|
+
timeout: 120_000,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const content = response.data?.choices?.[0]?.message?.content;
|
|
140
|
+
if (typeof content !== 'string' || !content.trim()) {
|
|
141
|
+
throw new Error('Resposta inesperada do gateway: choices[0].message.content está ausente ou vazia.');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { content, response: response.data, requestedModel: model };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function execute(ctx: AdapterExecutionContext): Promise<AdapterExecutionResult> {
|
|
148
|
+
const config = readConfig(ctx.config);
|
|
149
|
+
const prompt = getPrompt(ctx);
|
|
150
|
+
const messages = [
|
|
151
|
+
{
|
|
152
|
+
role: 'system',
|
|
153
|
+
content: `Você é o agente ${ctx.agent.name} do Paperclip. Responda ao contexto da execução de forma útil e objetiva.`,
|
|
154
|
+
},
|
|
155
|
+
{ role: 'user', content: prompt },
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const result = await callGateway(config, messages);
|
|
160
|
+
await ctx.onLog('stdout', `${result.content}\n`);
|
|
161
|
+
return {
|
|
162
|
+
exitCode: 0,
|
|
163
|
+
signal: null,
|
|
164
|
+
timedOut: false,
|
|
165
|
+
summary: result.content,
|
|
166
|
+
model: result.response.model ?? result.requestedModel,
|
|
167
|
+
provider: 'openai-compatible',
|
|
168
|
+
usage: result.response.usage ? {
|
|
169
|
+
inputTokens: result.response.usage.prompt_tokens ?? 0,
|
|
170
|
+
outputTokens: result.response.usage.completion_tokens ?? 0,
|
|
171
|
+
} : undefined,
|
|
172
|
+
};
|
|
80
173
|
} 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;
|
|
174
|
+
const message = friendlyError(err);
|
|
175
|
+
await ctx.onLog('stderr', `${message}\n`);
|
|
176
|
+
return { exitCode: 1, signal: null, timedOut: false, errorMessage: message, model: config.model as string | undefined };
|
|
100
177
|
}
|
|
101
178
|
}
|
|
102
179
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
180
|
+
async function testEnvironment(ctx: AdapterEnvironmentTestContext): Promise<AdapterEnvironmentTestResult> {
|
|
181
|
+
const config = readConfig(ctx.config);
|
|
182
|
+
const testedAt = new Date().toISOString();
|
|
183
|
+
try {
|
|
184
|
+
const { baseUrl, apiKey, model } = getConnection(config);
|
|
185
|
+
await axios.get(`${baseUrl}/models`, {
|
|
186
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
187
|
+
timeout: 15_000,
|
|
188
|
+
});
|
|
189
|
+
return {
|
|
190
|
+
adapterType: ctx.adapterType,
|
|
191
|
+
status: 'pass',
|
|
192
|
+
checks: [{ level: 'info', code: 'gateway_reachable', message: `Gateway acessível; modelo configurado: ${model}.` }],
|
|
193
|
+
testedAt,
|
|
194
|
+
};
|
|
195
|
+
} catch (err) {
|
|
196
|
+
const message = friendlyError(err);
|
|
197
|
+
return {
|
|
198
|
+
adapterType: ctx.adapterType,
|
|
199
|
+
status: 'fail',
|
|
200
|
+
checks: [{ level: 'error', code: 'gateway_check_failed', message, hint: 'Confira a Base URL, a API Key e se o gateway oferece GET /models.' }],
|
|
201
|
+
testedAt,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function createServerAdapter(): ServerAdapterModule {
|
|
207
|
+
return { type, label, models, agentConfigurationDoc, configSchema, execute, testEnvironment };
|
|
208
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export interface ConfigField {
|
|
2
|
+
key: string;
|
|
3
|
+
type: string;
|
|
4
|
+
label: string;
|
|
5
|
+
default?: unknown;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
secret?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AdapterExecutionContext {
|
|
12
|
+
runId: string;
|
|
13
|
+
agent: { id: string; companyId: string; name: string; adapterConfig: unknown };
|
|
14
|
+
runtime: { sessionId: string | null; sessionParams: Record<string, unknown> | null };
|
|
15
|
+
config: Record<string, unknown>;
|
|
16
|
+
context: Record<string, any>;
|
|
17
|
+
onLog: (stream: 'stdout' | 'stderr', chunk: string) => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AdapterExecutionResult {
|
|
21
|
+
exitCode: number | null;
|
|
22
|
+
signal: string | null;
|
|
23
|
+
timedOut: boolean;
|
|
24
|
+
errorMessage?: string | null;
|
|
25
|
+
summary?: string | null;
|
|
26
|
+
usage?: { inputTokens: number; outputTokens: number };
|
|
27
|
+
provider?: string | null;
|
|
28
|
+
model?: string | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AdapterEnvironmentTestContext {
|
|
32
|
+
adapterType: string;
|
|
33
|
+
config: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface AdapterEnvironmentTestResult {
|
|
37
|
+
adapterType: string;
|
|
38
|
+
status: 'pass' | 'warn' | 'fail';
|
|
39
|
+
checks: Array<{
|
|
40
|
+
level: 'info' | 'warn' | 'error';
|
|
41
|
+
code: string;
|
|
42
|
+
message: string;
|
|
43
|
+
hint?: string;
|
|
44
|
+
}>;
|
|
45
|
+
testedAt: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ServerAdapterModule {
|
|
49
|
+
type: string;
|
|
50
|
+
label?: string;
|
|
51
|
+
models: Array<{ id: string; label: string }>;
|
|
52
|
+
agentConfigurationDoc: string;
|
|
53
|
+
configSchema: ConfigField[];
|
|
54
|
+
execute(ctx: AdapterExecutionContext): Promise<AdapterExecutionResult>;
|
|
55
|
+
testEnvironment(ctx: AdapterEnvironmentTestContext): Promise<AdapterEnvironmentTestResult>;
|
|
56
|
+
}
|