Skip to content

Add Straico Chat Model Node #15881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';

export class StraicoApi implements ICredentialType {
name = 'straicoApi';

displayName = 'Straico API';

documentationUrl = 'https://documenter.getpostman.com/view/5900072/2s9YyzddrR';

properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
required: true,
default: '',
},
{
displayName: 'Base URL',
name: 'url',
type: 'hidden',
default: 'https://api.straico.com/v0',
},
];

authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '={{ "Bearer " + $credentials.apiKey }}',
},
},
};

test: ICredentialTestRequest = {
request: {
baseURL: '={{ $credentials.url }}',
url: '/models',
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ function getInputs(
'@n8n/n8n-nodes-langchain.lmChatDeepSeek',
'@n8n/n8n-nodes-langchain.lmChatOpenRouter',
'@n8n/n8n-nodes-langchain.lmChatXAiGrok',
'@n8n/n8n-nodes-langchain.lmChatStraico',
],
},
},
Expand Down Expand Up @@ -133,6 +134,7 @@ function getInputs(
'@n8n/n8n-nodes-langchain.lmChatDeepSeek',
'@n8n/n8n-nodes-langchain.lmChatOpenRouter',
'@n8n/n8n-nodes-langchain.lmChatXAiGrok',
'@n8n/n8n-nodes-langchain.lmChatStraico',
],
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ function getInputs(hasOutputParser?: boolean): Array<NodeConnectionType | INodeI
'@n8n/n8n-nodes-langchain.lmChatDeepSeek',
'@n8n/n8n-nodes-langchain.lmChatOpenRouter',
'@n8n/n8n-nodes-langchain.lmChatXAiGrok',
'@n8n/n8n-nodes-langchain.lmChatStraico',
],
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
import {
NodeConnectionTypes,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
import { getHttpProxyAgent } from '@utils/httpProxyAgent';
import { getConnectionHintNoticeField } from '@utils/sharedFields';
import { openAiFailedAttemptHandler } from '../../vendors/OpenAi/helpers/error-handling';
import { makeN8nLlmFailedAttemptHandler } from '../n8nLlmFailedAttemptHandler';
import { N8nLlmTracing } from '../N8nLlmTracing';
import { StraicoChatModel } from './StraicoChatModel';

interface OpenAICompatibleCredential {
apiKey: string;
url: string;
}

export class LmChatStraico implements INodeType {
description: INodeTypeDescription = {
displayName: 'Straico Chat Model',
name: 'lmChatStraico',
icon: { light: 'file:straico.svg', dark: 'file:straico.svg' },
group: ['transform'],
version: [1],
description: 'Expose Straico chat-completion models for AI chains and the AI-Agent',
defaults: { name: 'Straico Chat Model' },
codex: {
categories: ['AI'],
subcategories: {
AI: ['Language Models', 'Root Nodes'],
'Language Models': ['Chat Models (Recommended)'],
},
resources: {
primaryDocumentation: [
{
url: 'https://github.com/nlp-deutschland-de/n8n-docs/blob/main/docs/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmChatStraico.md',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiLanguageModel],
outputNames: ['Model'],
credentials: [
{
name: 'straicoApi',
required: true,
},
],
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL: '={{ $credentials?.url }}',
},
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),
{
displayName: 'Model',
name: 'model',
type: 'options',
required: true,
default: 'anthropic/claude-3.5-sonnet',
description: 'Choose the Straico model to generate the completion. <a href="https://documenter.getpostman.com/view/5900072/2s9YyzddrR">Learn more</a>.',
typeOptions: {
loadOptions: {
routing: {
request: {
method: 'GET',
url: '/models',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'data',
},
},
{
type: 'setKeyValue',
properties: {
name: '={{$responseItem.model}}',
value: '={{$responseItem.model}}',
},
},
{
type: 'sort',
properties: {
key: 'name',
},
},
],
},
},
},
},
routing: {
send: { type: 'body', property: 'model' },
},
},
{
displayName: 'Options',
name: 'options',
placeholder: 'Add Option',
type: 'collection',
default: {},
options: [
{
displayName: 'Temperature',
name: 'temperature',
type: 'number',
default: 1,
typeOptions: { minValue: 0, maxValue: 2 },
description: 'Controls randomness of the model output.',
},
{
displayName: 'Top P',
name: 'top_p',
type: 'number',
default: 1,
typeOptions: { minValue: 0, maxValue: 1 },
description: 'Controls diversity via nucleus sampling.',
},
{
displayName: 'Max Tokens',
name: 'max_tokens',
type: 'number',
default: 1024,
description: 'Maximum number of tokens to generate.',
},
{
displayName: 'Presence Penalty',
name: 'presence_penalty',
type: 'number',
default: 0,
typeOptions: { minValue: -2, maxValue: 2 },
description: 'Penalizes tokens based on their presence in the text.',
},
{
displayName: 'Frequency Penalty',
name: 'frequency_penalty',
type: 'number',
default: 0,
typeOptions: { minValue: -2, maxValue: 2 },
description: 'Penalizes tokens based on their frequency in the text.',
},
{
displayName: 'Timeout (ms)',
name: 'timeout',
type: 'number',
default: 60000,
description: 'Maximum amount of time a request is allowed to take in milliseconds.',
},
{
displayName: 'Max Retries',
name: 'maxRetries',
type: 'number',
default: 2,
description: 'Maximum number of retries to attempt.',
},
],
},
],
};

async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
const credentials = await this.getCredentials<OpenAICompatibleCredential>('straicoApi');
console.log('Credentials:', { apiKey: '***', url: credentials.url });

const modelName = this.getNodeParameter('model', itemIndex) as string;
console.log('Selected Model Name:', modelName); // Log the selected model

const rawOptions = this.getNodeParameter('options', itemIndex, {}) as Record<string, any>;

const options = {
temperature: rawOptions.temperature,
topP: rawOptions.top_p,
maxTokens: rawOptions.max_tokens,
presencePenalty: rawOptions.presence_penalty,
frequencyPenalty: rawOptions.frequency_penalty,
timeout: rawOptions.timeout ?? 60000,
maxRetries: rawOptions.maxRetries ?? 2,
};
console.log('Options:', options);

const model = new StraicoChatModel({
apiKey: credentials.apiKey,
baseURL: credentials.url || 'https://api.straico.com/v0',
modelName, // Use the selected model from the dropdown
...options,
callbacks: [new N8nLlmTracing(this)],
onFailedAttempt: makeN8nLlmFailedAttemptHandler(this, openAiFailedAttemptHandler),
});

return { response: model };
}
}
Loading