first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,114 @@
import { OpenAI } from '@langchain/openai';
import {
NodeConnectionTypes,
type INodeType,
type INodeTypeDescription,
type ISupplyDataFunctions,
type SupplyData,
} from 'n8n-workflow';
import type { LemonadeApiCredentialsType } from '../../../credentials/LemonadeApi.credentials';
import { lemonadeDescription, lemonadeModel, lemonadeOptions } from './description';
import {
makeN8nLlmFailedAttemptHandler,
N8nLlmTracing,
getConnectionHintNoticeField,
} from '@n8n/ai-utilities';
export class LmLemonade implements INodeType {
description: INodeTypeDescription = {
displayName: 'Lemonade Model',
name: 'lmLemonade',
icon: 'file:lemonade.svg',
group: ['transform'],
version: 1,
description: 'Language Model Lemonade',
defaults: {
name: 'Lemonade Model',
},
codex: {
categories: ['AI'],
subcategories: {
AI: ['Language Models', 'Root Nodes'],
'Language Models': ['Text Completion Models'],
},
resources: {
primaryDocumentation: [
{
url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmlemonade/',
},
],
},
},
inputs: [],
outputs: [NodeConnectionTypes.AiLanguageModel],
outputNames: ['Model'],
...lemonadeDescription,
properties: [
getConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),
lemonadeModel,
lemonadeOptions,
],
};
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
const credentials = (await this.getCredentials('lemonadeApi')) as LemonadeApiCredentialsType;
const modelName = this.getNodeParameter('model', itemIndex) as string;
const options = this.getNodeParameter('options', itemIndex, {}) as {
temperature?: number;
topP?: number;
frequencyPenalty?: number;
presencePenalty?: number;
maxTokens?: number;
stop?: string;
};
// Process stop sequences
let stop: string[] | undefined;
if (options.stop) {
const stopSequences = options.stop
.split(',')
.map((s) => s.trim())
.filter((s) => s.length > 0);
stop = stopSequences.length > 0 ? stopSequences : undefined;
}
// Ensure we have an API key for OpenAI client validation
const apiKey = credentials.apiKey || 'lemonade-placeholder-key';
// Build configuration object separately like official OpenAI node
const configuration: any = {
baseURL: credentials.baseUrl,
};
// Add custom headers if API key is provided
if (credentials.apiKey) {
configuration.defaultHeaders = {
Authorization: `Bearer ${credentials.apiKey}`,
};
}
const model = new OpenAI({
apiKey,
model: modelName,
temperature: options.temperature,
topP: options.topP,
frequencyPenalty: options.frequencyPenalty,
presencePenalty: options.presencePenalty,
maxTokens: options.maxTokens && options.maxTokens > 0 ? options.maxTokens : undefined,
stop,
configuration,
callbacks: [new N8nLlmTracing(this)],
onFailedAttempt: makeN8nLlmFailedAttemptHandler(this),
});
return {
response: model,
};
}
}
@@ -0,0 +1,125 @@
import type { INodeProperties, INodeTypeDescription } from 'n8n-workflow';
export const lemonadeDescription: Partial<INodeTypeDescription> = {
credentials: [
{
name: 'lemonadeApi',
required: true,
},
],
requestDefaults: {
ignoreHttpStatusErrors: true,
baseURL: '={{ $credentials.baseUrl.replace(new RegExp("/$"), "") }}',
},
};
export const lemonadeModel: INodeProperties = {
displayName: 'Model',
name: 'model',
type: 'options',
default: '',
description:
'The model which will generate the completion. Models are loaded and managed through the Lemonade server.',
typeOptions: {
loadOptions: {
routing: {
request: {
method: 'GET',
url: '/models',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'data',
},
},
{
type: 'setKeyValue',
properties: {
name: '={{$responseItem.id}}',
value: '={{$responseItem.id}}',
},
},
{
type: 'sort',
properties: {
key: 'name',
},
},
],
},
},
},
},
routing: {
send: {
type: 'body',
property: 'model',
},
},
required: true,
};
export const lemonadeOptions: INodeProperties = {
displayName: 'Options',
name: 'options',
placeholder: 'Add Option',
description: 'Additional options to add',
type: 'collection',
default: {},
options: [
{
displayName: 'Sampling Temperature',
name: 'temperature',
default: 0.7,
typeOptions: { maxValue: 2, minValue: 0, numberPrecision: 1 },
description:
'Controls the randomness of the generated text. Lower values make the output more focused and deterministic, while higher values make it more diverse and random.',
type: 'number',
},
{
displayName: 'Top P',
name: 'topP',
default: 1,
typeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },
description:
'Chooses from the smallest possible set of tokens whose cumulative probability exceeds the probability top_p. Helps generate more human-like text by reducing repetitions.',
type: 'number',
},
{
displayName: 'Frequency Penalty',
name: 'frequencyPenalty',
type: 'number',
default: 0.0,
typeOptions: { minValue: -2, maxValue: 2, numberPrecision: 1 },
description:
'Adjusts the penalty for tokens that have already appeared in the generated text. Positive values discourage repetition, negative values encourage it.',
},
{
displayName: 'Presence Penalty',
name: 'presencePenalty',
type: 'number',
default: 0.0,
typeOptions: { minValue: -2, maxValue: 2, numberPrecision: 1 },
description:
'Adjusts the penalty for tokens based on their presence in the generated text so far. Positive values penalize tokens that have already appeared, encouraging diversity.',
},
{
displayName: 'Max Tokens to Generate',
name: 'maxTokens',
type: 'number',
default: -1,
description:
'The maximum number of tokens to generate. Set to -1 for no limit. Be cautious when setting this to a large value, as it can lead to very long outputs.',
},
{
displayName: 'Stop Sequences',
name: 'stop',
type: 'string',
default: '',
description: 'Comma-separated list of sequences where the model will stop generating text',
},
],
};
@@ -0,0 +1,53 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.03604 2.49169L2 5.00962C2.82591 7.34634 5.52523 8.53484 8.04325 7.52763L14.0865 5.00967C13.2606 2.66287 9.85018 1.17686 7.03604 2.49169Z" fill="url(#paint0_linear_18_30102)"/>
<g filter="url(#filter0_f_18_30102)">
<path d="M2.64575 5.26035L7.22767 2.96947C8.5803 2.39118 10.05 2.55851 11.2129 2.97773C12.2111 3.33758 12.9907 3.9608 13.418 4.75328L7.85198 7.0724C5.7348 7.91746 3.52324 7.0363 2.64575 5.26035Z" fill="url(#paint1_linear_18_30102)"/>
</g>
<g filter="url(#filter1_f_18_30102)">
<path d="M5.00464 4.17246L7.43032 2.76879C8.78294 2.1905 10.2527 2.35783 11.4155 2.77705C12.4137 3.13689 13.1933 3.76012 13.6206 4.5526C10.4511 2.99575 9.9351 2.43024 5.00464 4.17246Z" fill="url(#paint2_linear_18_30102)"/>
</g>
<path d="M14.9236 4.5997C9.51985 6.50701 6.6904 12.4499 8.59216 17.8694L9.84454 21.4282C11.2477 25.4172 14.8309 28.0107 18.7736 28.3363C19.5157 28.3945 20.2115 28.7201 20.7681 29.2202C21.5682 29.9413 22.7162 30.2087 23.7947 29.8249C24.8731 29.4412 25.6037 28.5108 25.7776 27.4525C25.8936 26.7082 26.2299 26.0336 26.7749 25.5103C29.6391 22.7656 30.8103 18.4974 29.4072 14.5084L28.1548 10.9496C26.2531 5.51849 20.3274 2.68077 14.9236 4.5997Z" fill="url(#paint3_radial_18_30102)"/>
<path d="M14.9236 4.5997C9.51985 6.50701 6.6904 12.4499 8.59216 17.8694L9.84454 21.4282C11.2477 25.4172 14.8309 28.0107 18.7736 28.3363C19.5157 28.3945 20.2115 28.7201 20.7681 29.2202C21.5682 29.9413 22.7162 30.2087 23.7947 29.8249C24.8731 29.4412 25.6037 28.5108 25.7776 27.4525C25.8936 26.7082 26.2299 26.0336 26.7749 25.5103C29.6391 22.7656 30.8103 18.4974 29.4072 14.5084L28.1548 10.9496C26.2531 5.51849 20.3274 2.68077 14.9236 4.5997Z" fill="url(#paint4_radial_18_30102)"/>
<path d="M14.9236 4.5997C9.51985 6.50701 6.6904 12.4499 8.59216 17.8694L9.84454 21.4282C11.2477 25.4172 14.8309 28.0107 18.7736 28.3363C19.5157 28.3945 20.2115 28.7201 20.7681 29.2202C21.5682 29.9413 22.7162 30.2087 23.7947 29.8249C24.8731 29.4412 25.6037 28.5108 25.7776 27.4525C25.8936 26.7082 26.2299 26.0336 26.7749 25.5103C29.6391 22.7656 30.8103 18.4974 29.4072 14.5084L28.1548 10.9496C26.2531 5.51849 20.3274 2.68077 14.9236 4.5997Z" fill="url(#paint5_radial_18_30102)"/>
<defs>
<filter id="filter0_f_18_30102" x="1.89035" y="1.84127" width="12.283" height="6.31025" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.377703" result="effect1_foregroundBlur_18_30102"/>
</filter>
<filter id="filter1_f_18_30102" x="4.24923" y="1.64059" width="10.1268" height="3.66743" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.377703" result="effect1_foregroundBlur_18_30102"/>
</filter>
<linearGradient id="paint0_linear_18_30102" x1="2" y1="5.00899" x2="14.0865" y2="5.00899" gradientUnits="userSpaceOnUse">
<stop stop-color="#80A338"/>
<stop offset="1" stop-color="#B3D745"/>
</linearGradient>
<linearGradient id="paint1_linear_18_30102" x1="1.99902" y1="5.02002" x2="14.0855" y2="5.02002" gradientUnits="userSpaceOnUse">
<stop stop-color="#95BD27"/>
<stop offset="1" stop-color="#BAE038"/>
</linearGradient>
<linearGradient id="paint2_linear_18_30102" x1="13.6206" y1="4.2397" x2="6.38307" y2="3.29833" gradientUnits="userSpaceOnUse">
<stop stop-color="#D1F56E" stop-opacity="0"/>
<stop offset="0.286062" stop-color="#D1F56E"/>
<stop offset="1" stop-color="#D1F56E" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint3_radial_18_30102" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(21.2025 10.5724) rotate(115.148) scale(17.6305 14.9181)">
<stop stop-color="#FFFB98"/>
<stop offset="0.505208" stop-color="#FFD84C"/>
<stop offset="1" stop-color="#E6B534"/>
</radialGradient>
<radialGradient id="paint4_radial_18_30102" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(14.6238 4.96834) rotate(69.3343) scale(26.7531 22.6372)">
<stop offset="0.521583" stop-color="#FFDE67" stop-opacity="0"/>
<stop offset="0.736095" stop-color="#FFA457" stop-opacity="0.2"/>
<stop offset="0.886173" stop-color="#D5676D" stop-opacity="0.75"/>
<stop offset="0.917885" stop-color="#E88257"/>
<stop offset="1" stop-color="#F49754"/>
</radialGradient>
<radialGradient id="paint5_radial_18_30102" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-10.5942 -28.473) rotate(56.1215) scale(51.3589 43.4576)">
<stop offset="0.707976" stop-color="#D5B638"/>
<stop offset="0.873737" stop-color="#D5B638" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB