@luanpoppe/ai 1.0.5 → 1.0.7
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/.env.example +10 -0
- package/dist/@types/model-names.d.ts +4 -1
- package/dist/@types/model-names.d.ts.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -2
- package/dist/index.js.map +1 -1
- package/dist/langchain/models.d.ts +4 -0
- package/dist/langchain/models.d.ts.map +1 -1
- package/dist/langchain/models.js +17 -0
- package/dist/langchain/models.js.map +1 -1
- package/dist/langchain/tools.d.ts +1 -1
- package/package.json +19 -9
- package/src/@types/model-names.ts +17 -1
- package/src/index.ts +63 -3
- package/src/langchain/models.ts +22 -0
- package/tests/e2e/README.md +47 -0
- package/tests/e2e/langchain.test.ts +523 -0
- package/tests/tsconfig.json +17 -0
- package/tests/unit/index.test.ts +355 -0
- package/tests/unit/langchain/messages.test.ts +101 -0
- package/tests/unit/langchain/models.test.ts +192 -0
- package/tests/unit/langchain/tools.test.ts +134 -0
- package/vitest.config.ts +24 -0
package/.env.example
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Variáveis de ambiente necessárias APENAS para os testes E2E
|
|
2
|
+
|
|
3
|
+
# OpenAI API Key (obrigatória para testes com GPT)
|
|
4
|
+
OPENAI_API_KEY=sk-...
|
|
5
|
+
|
|
6
|
+
# Google Gemini Token (obrigatória para testes com Gemini)
|
|
7
|
+
GOOGLE_GEMINI_TOKEN=...
|
|
8
|
+
|
|
9
|
+
# Obrigatória para testes com OpenRouter
|
|
10
|
+
OPENROUTER_API_KEY=...
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
type ChatGPTModels = "gpt-4" | "gpt-4o" | "gpt-4.1" | "gpt-5" | "gpt-5.1" | "gpt-5-mini" | "gpt-5-nano";
|
|
2
2
|
type GeminiModels = "gemini-2.5-flash" | "gemini-2.5-pro" | "gemini-3-flash" | "gemini-3-pro";
|
|
3
|
-
|
|
3
|
+
type AnthropicModels = "claude-opus-4.5" | "claude-haiku-4.5" | "claude-sonnet-4.5" | "claude-opus-4.1" | "claude-opus-4" | "claude-sonnet-4";
|
|
4
|
+
type OpenRouterProvidersModels = `google/${GeminiModels}` | `openai/${ChatGPTModels}` | `anthropic/${AnthropicModels}`;
|
|
5
|
+
type OpenRouterModels = `openrouter:${OpenRouterProvidersModels}` | `openrouter/${OpenRouterProvidersModels}`;
|
|
6
|
+
export type AIModelNames = ChatGPTModels | GeminiModels | OpenRouterModels;
|
|
4
7
|
export {};
|
|
5
8
|
//# sourceMappingURL=model-names.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-names.d.ts","sourceRoot":"","sources":["../../src/@types/model-names.ts"],"names":[],"mappings":"AAAA,KAAK,aAAa,GACd,OAAO,GACP,QAAQ,GACR,SAAS,GACT,OAAO,GACP,SAAS,GACT,YAAY,GACZ,YAAY,CAAC;AAEjB,KAAK,YAAY,GACb,kBAAkB,GAClB,gBAAgB,GAChB,gBAAgB,GAChB,cAAc,CAAC;AAEnB,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"model-names.d.ts","sourceRoot":"","sources":["../../src/@types/model-names.ts"],"names":[],"mappings":"AAAA,KAAK,aAAa,GACd,OAAO,GACP,QAAQ,GACR,SAAS,GACT,OAAO,GACP,SAAS,GACT,YAAY,GACZ,YAAY,CAAC;AAEjB,KAAK,YAAY,GACb,kBAAkB,GAClB,gBAAgB,GAChB,gBAAgB,GAChB,cAAc,CAAC;AAEnB,KAAK,eAAe,GAAG,iBAAiB,GACpC,kBAAkB,GAClB,mBAAmB,GACnB,iBAAiB,GACjB,eAAe,GACf,iBAAiB,CAAC;AAEtB,KAAK,yBAAyB,GAC1B,UAAU,YAAY,EAAE,GACxB,UAAU,aAAa,EAAE,GACzB,aAAa,eAAe,EAAE,CAAA;AAElC,KAAK,gBAAgB,GACjB,cAAc,yBAAyB,EAAE,GACzC,cAAc,yBAAyB,EAAE,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,YAAY,GAAG,gBAAgB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { LangchainTools } from "./langchain/tools";
|
|
|
9
9
|
type LangchainConstructor = {
|
|
10
10
|
googleGeminiToken?: string;
|
|
11
11
|
openAIApiKey?: string;
|
|
12
|
+
openRouterApiKey?: string;
|
|
12
13
|
};
|
|
13
14
|
export type LangchainCallParams = {
|
|
14
15
|
agent?: {
|
|
@@ -36,8 +37,14 @@ export declare class Langchain {
|
|
|
36
37
|
constructor(tokens: LangchainConstructor);
|
|
37
38
|
call(params: LangchainCallParams): LangchainCallReturn;
|
|
38
39
|
callStructuredOutput<T extends z.ZodSchema>(params: LangchainCallStructuredOutputParams<T>): LangchainCallStructuredOutputReturn<typeof params.outputSchema>;
|
|
40
|
+
/**
|
|
41
|
+
* Normaliza schemas Zod para compatibilidade com OpenAI/OpenRouter
|
|
42
|
+
* OpenAI exige que todos os campos em properties estejam no array required
|
|
43
|
+
* quando usa response_format: 'extract'
|
|
44
|
+
*/
|
|
45
|
+
private normalizeSchemaForOpenAI;
|
|
39
46
|
getRawAgent(params: LangchainCallParams, outputSchema?: z.ZodSchema | undefined): {
|
|
40
|
-
agent: import("langchain").ReactAgent<Record<string, any>, import("@langchain/core/utils/types").InteropZodObject | import("langchain").AnyAnnotationRoot | undefined, import("@langchain/core/utils/types").InteropZodObject | import("langchain").AnyAnnotationRoot, readonly AgentMiddleware<any, any, any>[]
|
|
47
|
+
agent: import("langchain").ReactAgent<import("langchain").AgentTypeConfig<Record<string, any>, import("@langchain/core/utils/types").InteropZodObject | import("langchain").AnyAnnotationRoot | undefined, import("@langchain/core/utils/types").InteropZodObject | import("langchain").AnyAnnotationRoot, readonly AgentMiddleware<any, any, any, readonly (ClientTool | ServerTool)[]>[], readonly (ClientTool | ServerTool)[]>>;
|
|
41
48
|
};
|
|
42
49
|
private getModel;
|
|
43
50
|
private standardAgent;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EACL,eAAe,EACf,WAAW,EAIZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,KAAK,oBAAoB,GAAG;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EACL,eAAe,EACf,WAAW,EAIZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,KAAK,oBAAoB,GAAG;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;QAC/B,KAAK,CAAC,EAAE,CAAC,UAAU,GAAG,UAAU,CAAC,EAAE,CAAC;KACrC,CAAC;IAEF,WAAW,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC;IAEvD,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB,CAAC,CAAC;AAEH,MAAM,MAAM,mCAAmC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,IACnE,mBAAmB,GAAG;IACpB,YAAY,EAAE,CAAC,CAAC;CACjB,CAAC;AAEJ,MAAM,MAAM,mCAAmC,CAAC,CAAC,IAAI,OAAO,CAAC;IAC3D,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CACtB,CAAC,CAAC;AAEH,qBAAa,SAAS;IACR,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,oBAAoB;IAE1C,IAAI,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB;IAiBtD,oBAAoB,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,EAC9C,MAAM,EAAE,mCAAmC,CAAC,CAAC,CAAC,GAC7C,mCAAmC,CAAC,OAAO,MAAM,CAAC,YAAY,CAAC;IAwBlE;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IAsChC,WAAW,CACT,MAAM,EAAE,mBAAmB,EAC3B,YAAY,CAAC,EAAE,CAAC,CAAC,SAAS,GAAG,SAAS;;;IAUxC,OAAO,CAAC,QAAQ;IAiChB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,mBAAmB;CAU5B;AAED,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.LangchainTools = exports.LangchainMessages = exports.LangchainModels = exports.Langchain = void 0;
|
|
4
7
|
const models_1 = require("./langchain/models");
|
|
5
8
|
Object.defineProperty(exports, "LangchainModels", { enumerable: true, get: function () { return models_1.LangchainModels; } });
|
|
9
|
+
const zod_1 = __importDefault(require("zod"));
|
|
6
10
|
const langchain_1 = require("langchain");
|
|
7
11
|
const messages_1 = require("./langchain/messages");
|
|
8
12
|
Object.defineProperty(exports, "LangchainMessages", { enumerable: true, get: function () { return messages_1.LangchainMessages; } });
|
|
@@ -26,10 +30,13 @@ class Langchain {
|
|
|
26
30
|
};
|
|
27
31
|
}
|
|
28
32
|
async callStructuredOutput(params) {
|
|
29
|
-
const { outputSchema, messages } = params;
|
|
33
|
+
const { outputSchema, messages, aiModel } = params;
|
|
34
|
+
// Normaliza o schema para compatibilidade com OpenAI/OpenRouter
|
|
35
|
+
// OpenAI exige que todos os campos em properties estejam no array required
|
|
36
|
+
const normalizedSchema = this.normalizeSchemaForOpenAI(outputSchema, aiModel);
|
|
30
37
|
const agent = (0, langchain_1.createAgent)({
|
|
31
38
|
...this.standardAgent(params),
|
|
32
|
-
responseFormat:
|
|
39
|
+
responseFormat: normalizedSchema,
|
|
33
40
|
});
|
|
34
41
|
const response = await agent.invoke({
|
|
35
42
|
messages,
|
|
@@ -37,6 +44,40 @@ class Langchain {
|
|
|
37
44
|
const parsedResponse = outputSchema.parse(response?.structuredResponse);
|
|
38
45
|
return { response: parsedResponse };
|
|
39
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Normaliza schemas Zod para compatibilidade com OpenAI/OpenRouter
|
|
49
|
+
* OpenAI exige que todos os campos em properties estejam no array required
|
|
50
|
+
* quando usa response_format: 'extract'
|
|
51
|
+
*/
|
|
52
|
+
normalizeSchemaForOpenAI(schema, aiModel) {
|
|
53
|
+
// Apenas normaliza para modelos OpenAI/OpenRouter
|
|
54
|
+
const isOpenAIModel = aiModel.startsWith("gpt") ||
|
|
55
|
+
aiModel.startsWith("openrouter:openai/") ||
|
|
56
|
+
aiModel.startsWith("openrouter/openai/");
|
|
57
|
+
if (!isOpenAIModel) {
|
|
58
|
+
return schema;
|
|
59
|
+
}
|
|
60
|
+
// Se o schema é um objeto Zod, precisamos normalizar campos opcionais
|
|
61
|
+
if (schema instanceof zod_1.default.ZodObject) {
|
|
62
|
+
const shape = schema.shape;
|
|
63
|
+
const newShape = {};
|
|
64
|
+
// Converte campos opcionais para nullable para compatibilidade com OpenAI
|
|
65
|
+
// OpenAI requer que todos os campos estejam no array required
|
|
66
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
67
|
+
if (value instanceof zod_1.default.ZodOptional) {
|
|
68
|
+
// Converte .optional() para .nullable() para compatibilidade com OpenAI
|
|
69
|
+
const innerType = value._def.innerType;
|
|
70
|
+
// Usa z.union para criar um tipo nullable
|
|
71
|
+
newShape[key] = zod_1.default.union([innerType, zod_1.default.null()]);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
newShape[key] = value;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return zod_1.default.object(newShape);
|
|
78
|
+
}
|
|
79
|
+
return schema;
|
|
80
|
+
}
|
|
40
81
|
getRawAgent(params, outputSchema) {
|
|
41
82
|
const agent = (0, langchain_1.createAgent)({
|
|
42
83
|
...this.standardAgent(params),
|
|
@@ -59,6 +100,14 @@ class Langchain {
|
|
|
59
100
|
config.apiKey = this.tokens.googleGeminiToken;
|
|
60
101
|
return models_1.LangchainModels.gemini(config);
|
|
61
102
|
}
|
|
103
|
+
if (aiModel.startsWith("openrouter:") || aiModel.startsWith("openrouter/")) {
|
|
104
|
+
const modelName = aiModel.replace(/^openrouter[:/]/, "");
|
|
105
|
+
return models_1.LangchainModels.openrouter({
|
|
106
|
+
...config,
|
|
107
|
+
model: modelName,
|
|
108
|
+
apiKey: this.tokens.openRouterApiKey,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
62
111
|
throw new Error("Model not supported");
|
|
63
112
|
}
|
|
64
113
|
standardAgent(params) {
|
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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"}
|
|
@@ -9,5 +9,9 @@ export type LLMModelConfig = {
|
|
|
9
9
|
export declare class LangchainModels {
|
|
10
10
|
static gpt(params: LLMModelConfig): ChatOpenAI<import("@langchain/openai").ChatOpenAICallOptions>;
|
|
11
11
|
static gemini(params: LLMModelConfig): ChatGoogleGenerativeAI;
|
|
12
|
+
static openrouter(params: LLMModelConfig & {
|
|
13
|
+
httpReferer?: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
}): ChatOpenAI<import("@langchain/openai").ChatOpenAICallOptions>;
|
|
12
16
|
}
|
|
13
17
|
//# sourceMappingURL=models.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/langchain/models.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EAEvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AAEjE,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF,qBAAa,eAAe;IAC1B,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc;IAgBjC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc;
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/langchain/models.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EAEvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AAEjE,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF,qBAAa,eAAe;IAC1B,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc;IAgBjC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc;IAmBpC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;CAqBpF"}
|
package/dist/langchain/models.js
CHANGED
|
@@ -32,6 +32,23 @@ class LangchainModels {
|
|
|
32
32
|
options.temperature = temperature;
|
|
33
33
|
return new google_genai_1.ChatGoogleGenerativeAI(options);
|
|
34
34
|
}
|
|
35
|
+
static openrouter(params) {
|
|
36
|
+
const { apiKey, maxTokens, model, temperature, httpReferer, title } = params;
|
|
37
|
+
if (!apiKey)
|
|
38
|
+
throw new Error("OpenRouter API key is not passed in the model parameters");
|
|
39
|
+
const options = {
|
|
40
|
+
model,
|
|
41
|
+
apiKey,
|
|
42
|
+
configuration: {
|
|
43
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
if (maxTokens)
|
|
47
|
+
options.maxTokens = maxTokens;
|
|
48
|
+
if (temperature)
|
|
49
|
+
options.temperature = temperature;
|
|
50
|
+
return new openai_1.ChatOpenAI(options);
|
|
51
|
+
}
|
|
35
52
|
}
|
|
36
53
|
exports.LangchainModels = LangchainModels;
|
|
37
54
|
//# sourceMappingURL=models.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/langchain/models.ts"],"names":[],"mappings":";;;AAAA,0DAGiC;AACjC,8CAAiE;AASjE,MAAa,eAAe;IAC1B,MAAM,CAAC,GAAG,CAAC,MAAsB;QAC/B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QACzD,IAAI,CAAC,MAAM;YACT,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAE1E,MAAM,OAAO,GAAqB;YAChC,KAAK;YACL,MAAM;SACP,CAAC;QAEF,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;QAC7C,IAAI,WAAW;YAAE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;QAEnD,OAAO,IAAI,mBAAU,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,MAAsB;QAClC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QAEzD,IAAI,CAAC,MAAM;YACT,MAAM,IAAI,KAAK,CACb,6DAA6D,CAC9D,CAAC;QAEJ,MAAM,OAAO,GAAgC;YAC3C,KAAK;YACL,MAAM;SACP,CAAC;QAEF,IAAI,SAAS;YAAE,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;QACnD,IAAI,WAAW;YAAE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;QAEnD,OAAO,IAAI,qCAAsB,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/langchain/models.ts"],"names":[],"mappings":";;;AAAA,0DAGiC;AACjC,8CAAiE;AASjE,MAAa,eAAe;IAC1B,MAAM,CAAC,GAAG,CAAC,MAAsB;QAC/B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QACzD,IAAI,CAAC,MAAM;YACT,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAE1E,MAAM,OAAO,GAAqB;YAChC,KAAK;YACL,MAAM;SACP,CAAC;QAEF,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;QAC7C,IAAI,WAAW;YAAE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;QAEnD,OAAO,IAAI,mBAAU,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,MAAsB;QAClC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QAEzD,IAAI,CAAC,MAAM;YACT,MAAM,IAAI,KAAK,CACb,6DAA6D,CAC9D,CAAC;QAEJ,MAAM,OAAO,GAAgC;YAC3C,KAAK;YACL,MAAM;SACP,CAAC;QAEF,IAAI,SAAS;YAAE,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;QACnD,IAAI,WAAW;YAAE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;QAEnD,OAAO,IAAI,qCAAsB,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,MAAiE;QACjF,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;QAE7E,IAAI,CAAC,MAAM;YACT,MAAM,IAAI,KAAK,CACb,0DAA0D,CAC3D,CAAC;QAEJ,MAAM,OAAO,GAAqB;YAChC,KAAK;YACL,MAAM;YACN,aAAa,EAAE;gBACb,OAAO,EAAE,8BAA8B;aACxC;SACF,CAAC;QAEF,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;QAC7C,IAAI,WAAW;YAAE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;QAEnD,OAAO,IAAI,mBAAU,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;CACF;AAzDD,0CAyDC"}
|
|
@@ -7,6 +7,6 @@ export type CreateToolParams = {
|
|
|
7
7
|
schema: z.ZodSchema;
|
|
8
8
|
};
|
|
9
9
|
export declare class LangchainTools {
|
|
10
|
-
createTool(params: CreateToolParams): import("langchain").DynamicStructuredTool<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, unknown, unknown, unknown>;
|
|
10
|
+
createTool(params: CreateToolParams): import("langchain").DynamicStructuredTool<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, unknown, unknown, unknown, string>;
|
|
11
11
|
}
|
|
12
12
|
//# sourceMappingURL=tools.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luanpoppe/ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"keywords": [],
|
|
7
7
|
"author": "",
|
|
8
8
|
"license": "ISC",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@langchain/core": "^1.1.
|
|
11
|
-
"@langchain/google-genai": "^2.1.
|
|
12
|
-
"@langchain/openai": "^1.2.
|
|
13
|
-
"langchain": "^1.2.
|
|
14
|
-
"zod": "^4.
|
|
10
|
+
"@langchain/core": "^1.1.16",
|
|
11
|
+
"@langchain/google-genai": "^2.1.12",
|
|
12
|
+
"@langchain/openai": "^1.2.3",
|
|
13
|
+
"langchain": "^1.2.12",
|
|
14
|
+
"zod": "^4.3.6"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@types/node": "^25.0.
|
|
17
|
+
"@types/node": "^25.0.10",
|
|
18
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
19
|
+
"@vitest/ui": "^4.0.18",
|
|
20
|
+
"dotenv": "^16.4.7",
|
|
18
21
|
"tsx": "^4.21.0",
|
|
19
|
-
"typescript": "^5.9.3"
|
|
22
|
+
"typescript": "^5.9.3",
|
|
23
|
+
"vitest": "^4.0.18"
|
|
20
24
|
},
|
|
21
25
|
"scripts": {
|
|
22
26
|
"build": "tsc",
|
|
23
|
-
"pub": "pnpm build && pnpm publish --access=public"
|
|
27
|
+
"pub": "pnpm build && pnpm publish --access=public",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"test:ui": "vitest --ui",
|
|
31
|
+
"test:coverage": "vitest --coverage",
|
|
32
|
+
"test:e2e": "vitest run tests/e2e",
|
|
33
|
+
"test:unit": "vitest run tests/unit"
|
|
24
34
|
}
|
|
25
35
|
}
|
|
@@ -13,4 +13,20 @@ type GeminiModels =
|
|
|
13
13
|
| "gemini-3-flash"
|
|
14
14
|
| "gemini-3-pro";
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
type AnthropicModels = "claude-opus-4.5"
|
|
17
|
+
| "claude-haiku-4.5"
|
|
18
|
+
| "claude-sonnet-4.5"
|
|
19
|
+
| "claude-opus-4.1"
|
|
20
|
+
| "claude-opus-4"
|
|
21
|
+
| "claude-sonnet-4";
|
|
22
|
+
|
|
23
|
+
type OpenRouterProvidersModels =
|
|
24
|
+
| `google/${GeminiModels}`
|
|
25
|
+
| `openai/${ChatGPTModels}`
|
|
26
|
+
| `anthropic/${AnthropicModels}`
|
|
27
|
+
|
|
28
|
+
type OpenRouterModels =
|
|
29
|
+
| `openrouter:${OpenRouterProvidersModels}`
|
|
30
|
+
| `openrouter/${OpenRouterProvidersModels}`;
|
|
31
|
+
|
|
32
|
+
export type AIModelNames = ChatGPTModels | GeminiModels | OpenRouterModels;
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { LangchainTools } from "./langchain/tools";
|
|
|
16
16
|
type LangchainConstructor = {
|
|
17
17
|
googleGeminiToken?: string;
|
|
18
18
|
openAIApiKey?: string;
|
|
19
|
+
openRouterApiKey?: string;
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
export type LangchainCallParams = {
|
|
@@ -47,7 +48,7 @@ export type LangchainCallStructuredOutputReturn<T> = Promise<{
|
|
|
47
48
|
}>;
|
|
48
49
|
|
|
49
50
|
export class Langchain {
|
|
50
|
-
constructor(private tokens: LangchainConstructor) {}
|
|
51
|
+
constructor(private tokens: LangchainConstructor) { }
|
|
51
52
|
|
|
52
53
|
async call(params: LangchainCallParams): LangchainCallReturn {
|
|
53
54
|
const { messages } = params;
|
|
@@ -69,11 +70,18 @@ export class Langchain {
|
|
|
69
70
|
async callStructuredOutput<T extends z.ZodSchema>(
|
|
70
71
|
params: LangchainCallStructuredOutputParams<T>
|
|
71
72
|
): LangchainCallStructuredOutputReturn<typeof params.outputSchema> {
|
|
72
|
-
const { outputSchema, messages } = params;
|
|
73
|
+
const { outputSchema, messages, aiModel } = params;
|
|
74
|
+
|
|
75
|
+
// Normaliza o schema para compatibilidade com OpenAI/OpenRouter
|
|
76
|
+
// OpenAI exige que todos os campos em properties estejam no array required
|
|
77
|
+
const normalizedSchema = this.normalizeSchemaForOpenAI(
|
|
78
|
+
outputSchema,
|
|
79
|
+
aiModel
|
|
80
|
+
);
|
|
73
81
|
|
|
74
82
|
const agent = createAgent({
|
|
75
83
|
...this.standardAgent(params),
|
|
76
|
-
responseFormat:
|
|
84
|
+
responseFormat: normalizedSchema as any,
|
|
77
85
|
});
|
|
78
86
|
|
|
79
87
|
const response = await agent.invoke({
|
|
@@ -85,6 +93,49 @@ export class Langchain {
|
|
|
85
93
|
return { response: parsedResponse };
|
|
86
94
|
}
|
|
87
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Normaliza schemas Zod para compatibilidade com OpenAI/OpenRouter
|
|
98
|
+
* OpenAI exige que todos os campos em properties estejam no array required
|
|
99
|
+
* quando usa response_format: 'extract'
|
|
100
|
+
*/
|
|
101
|
+
private normalizeSchemaForOpenAI<T extends z.ZodSchema>(
|
|
102
|
+
schema: T,
|
|
103
|
+
aiModel: string
|
|
104
|
+
): z.ZodSchema {
|
|
105
|
+
// Apenas normaliza para modelos OpenAI/OpenRouter
|
|
106
|
+
const isOpenAIModel =
|
|
107
|
+
aiModel.startsWith("gpt") ||
|
|
108
|
+
aiModel.startsWith("openrouter:openai/") ||
|
|
109
|
+
aiModel.startsWith("openrouter/openai/");
|
|
110
|
+
|
|
111
|
+
if (!isOpenAIModel) {
|
|
112
|
+
return schema;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Se o schema é um objeto Zod, precisamos normalizar campos opcionais
|
|
116
|
+
if (schema instanceof z.ZodObject) {
|
|
117
|
+
const shape = schema.shape;
|
|
118
|
+
const newShape: Record<string, z.ZodTypeAny> = {};
|
|
119
|
+
|
|
120
|
+
// Converte campos opcionais para nullable para compatibilidade com OpenAI
|
|
121
|
+
// OpenAI requer que todos os campos estejam no array required
|
|
122
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
123
|
+
if (value instanceof z.ZodOptional) {
|
|
124
|
+
// Converte .optional() para .nullable() para compatibilidade com OpenAI
|
|
125
|
+
const innerType = value._def.innerType as z.ZodTypeAny;
|
|
126
|
+
// Usa z.union para criar um tipo nullable
|
|
127
|
+
newShape[key] = z.union([innerType, z.null()]);
|
|
128
|
+
} else {
|
|
129
|
+
newShape[key] = value;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return z.object(newShape);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return schema;
|
|
137
|
+
}
|
|
138
|
+
|
|
88
139
|
getRawAgent(
|
|
89
140
|
params: LangchainCallParams,
|
|
90
141
|
outputSchema?: z.ZodSchema | undefined
|
|
@@ -118,6 +169,15 @@ export class Langchain {
|
|
|
118
169
|
return LangchainModels.gemini(config);
|
|
119
170
|
}
|
|
120
171
|
|
|
172
|
+
if (aiModel.startsWith("openrouter:") || aiModel.startsWith("openrouter/")) {
|
|
173
|
+
const modelName = aiModel.replace(/^openrouter[:/]/, "");
|
|
174
|
+
return LangchainModels.openrouter({
|
|
175
|
+
...config,
|
|
176
|
+
model: modelName,
|
|
177
|
+
apiKey: this.tokens.openRouterApiKey,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
121
181
|
throw new Error("Model not supported");
|
|
122
182
|
}
|
|
123
183
|
|
package/src/langchain/models.ts
CHANGED
|
@@ -46,4 +46,26 @@ export class LangchainModels {
|
|
|
46
46
|
|
|
47
47
|
return new ChatGoogleGenerativeAI(options);
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
static openrouter(params: LLMModelConfig & { httpReferer?: string; title?: string }) {
|
|
51
|
+
const { apiKey, maxTokens, model, temperature, httpReferer, title } = params;
|
|
52
|
+
|
|
53
|
+
if (!apiKey)
|
|
54
|
+
throw new Error(
|
|
55
|
+
"OpenRouter API key is not passed in the model parameters"
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const options: ChatOpenAIFields = {
|
|
59
|
+
model,
|
|
60
|
+
apiKey,
|
|
61
|
+
configuration: {
|
|
62
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
if (maxTokens) options.maxTokens = maxTokens;
|
|
67
|
+
if (temperature) options.temperature = temperature;
|
|
68
|
+
|
|
69
|
+
return new ChatOpenAI(options);
|
|
70
|
+
}
|
|
49
71
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Testes End-to-End (E2E)
|
|
2
|
+
|
|
3
|
+
Estes testes fazem chamadas reais para as APIs das LLMs (OpenAI e Google Gemini).
|
|
4
|
+
|
|
5
|
+
## Configuração
|
|
6
|
+
|
|
7
|
+
### Onde colocar o arquivo .env?
|
|
8
|
+
|
|
9
|
+
**Coloque o arquivo `.env` na raiz do pacote `packages/ai/`** (não dentro de `tests/e2e/`).
|
|
10
|
+
|
|
11
|
+
1. Copie o arquivo de exemplo:
|
|
12
|
+
```bash
|
|
13
|
+
cd packages/ai
|
|
14
|
+
cp tests/e2e/env.example .env
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
2. Preencha as variáveis de ambiente no arquivo `.env`:
|
|
18
|
+
- `OPENAI_API_KEY`: Sua chave da API da OpenAI (necessária para testes com GPT)
|
|
19
|
+
- `GOOGLE_GEMINI_TOKEN`: Seu token da API do Google Gemini (necessária para testes com Gemini)
|
|
20
|
+
- `OPENROUTER_API_KEY`: Sua chave da API do OpenRouter (necessária para testes com OpenRouter)
|
|
21
|
+
|
|
22
|
+
### Como funciona?
|
|
23
|
+
|
|
24
|
+
O arquivo `.env` é carregado automaticamente pelo `dotenv` configurado no `vitest.config.ts`. O arquivo `.env` deve estar em `packages/ai/.env` e será carregado quando você executar `pnpm test:e2e`.
|
|
25
|
+
|
|
26
|
+
## Executando os testes
|
|
27
|
+
|
|
28
|
+
### Todos os testes E2E
|
|
29
|
+
```bash
|
|
30
|
+
pnpm test:e2e
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Apenas testes unitários
|
|
34
|
+
```bash
|
|
35
|
+
pnpm test:unit
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Todos os testes (unitários + E2E)
|
|
39
|
+
```bash
|
|
40
|
+
pnpm test
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Notas
|
|
44
|
+
|
|
45
|
+
- Os testes E2E são marcados com `skipIf` e só serão executados se as respectivas API keys estiverem configuradas
|
|
46
|
+
- Os testes E2E têm timeout de 30 segundos devido às chamadas de API
|
|
47
|
+
- **Atenção**: Estes testes consomem créditos das suas APIs. Use com moderação em desenvolvimento
|