Compare commits

...

1 Commits

Author SHA1 Message Date
Matthias Nannt
4a141171c5 chore: introduce ai package 2025-08-06 13:12:46 +02:00
16 changed files with 962 additions and 7 deletions

View File

@@ -124,6 +124,7 @@
"add_action": "Aktion hinzufügen",
"add_filter": "Filter hinzufügen",
"add_logo": "Logo hinzufügen",
"add_member": "Mitglied hinzufügen",
"add_project": "Projekt hinzufügen",
"add_to_team": "Zum Team hinzufügen",
"all": "Alle",
@@ -280,6 +281,7 @@
"only_one_file_allowed": "Es ist nur eine Datei erlaubt",
"only_owners_managers_and_manage_access_members_can_perform_this_action": "Nur Eigentümer, Manager und Mitglieder mit Zugriff auf das Management können diese Aktion ausführen.",
"option_id": "Option-ID",
"option_ids": "Option-IDs",
"or": "oder",
"organization": "Organisation",
"organization_id": "Organisations-ID",
@@ -1314,7 +1316,7 @@
"columns": "Spalten",
"company": "Firma",
"company_logo": "Firmenlogo",
"completed_responses": "abgeschlossene Antworten",
"completed_responses": "unvollständige oder vollständige Antworten.",
"concat": "Verketten +",
"conditional_logic": "Bedingte Logik",
"confirm_default_language": "Standardsprache bestätigen",

View File

@@ -124,6 +124,7 @@
"add_action": "Add action",
"add_filter": "Add filter",
"add_logo": "Add logo",
"add_member": "Add member",
"add_project": "Add project",
"add_to_team": "Add to team",
"all": "All",
@@ -280,6 +281,7 @@
"only_one_file_allowed": "Only one file is allowed",
"only_owners_managers_and_manage_access_members_can_perform_this_action": "Only owners and managers can perform this action.",
"option_id": "Option ID",
"option_ids": "Option IDs",
"or": "or",
"organization": "Organization",
"organization_id": "Organization ID",
@@ -1314,7 +1316,7 @@
"columns": "Columns",
"company": "Company",
"company_logo": "Company logo",
"completed_responses": "completed responses.",
"completed_responses": "partial or completed responses.",
"concat": "Concat +",
"conditional_logic": "Conditional Logic",
"confirm_default_language": "Confirm default language",

View File

@@ -124,6 +124,7 @@
"add_action": "Ajouter une action",
"add_filter": "Ajouter un filtre",
"add_logo": "Ajouter un logo",
"add_member": "Ajouter un membre",
"add_project": "Ajouter un projet",
"add_to_team": "Ajouter à l'équipe",
"all": "Tout",
@@ -280,6 +281,7 @@
"only_one_file_allowed": "Un seul fichier est autorisé",
"only_owners_managers_and_manage_access_members_can_perform_this_action": "Seules les propriétaires, les gestionnaires et les membres ayant accès à la gestion peuvent effectuer cette action.",
"option_id": "Identifiant de l'option",
"option_ids": "Identifiants des options",
"or": "ou",
"organization": "Organisation",
"organization_id": "ID de l'organisation",
@@ -1205,7 +1207,7 @@
"delete_survey_and_responses_warning": "Êtes-vous sûr de vouloir supprimer cette enquête et toutes ses réponses?",
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Choisissez la langue par défaut pour ce sondage :",
"2_activate_translation_for_specific_languages": "2. Activer la traduction pour des langues spécifiques :",
"2_activate_translation_for_specific_languages": "2. Activer la traduction pour des langues spécifiques:",
"add": "Ajouter +",
"add_a_delay_or_auto_close_the_survey": "Ajouter un délai ou fermer automatiquement l'enquête",
"add_a_four_digit_pin": "Ajoutez un code PIN à quatre chiffres.",
@@ -1314,7 +1316,7 @@
"columns": "Colonnes",
"company": "Société",
"company_logo": "Logo de l'entreprise",
"completed_responses": "réponses complètes.",
"completed_responses": "des réponses partielles ou complètes.",
"concat": "Concat +",
"conditional_logic": "Logique conditionnelle",
"confirm_default_language": "Confirmer la langue par défaut",

View File

@@ -124,6 +124,7 @@
"add_action": "Adicionar ação",
"add_filter": "Adicionar filtro",
"add_logo": "Adicionar logo",
"add_member": "Adicionar membro",
"add_project": "Adicionar projeto",
"add_to_team": "Adicionar à equipe",
"all": "Todos",
@@ -280,6 +281,7 @@
"only_one_file_allowed": "É permitido apenas um arquivo",
"only_owners_managers_and_manage_access_members_can_perform_this_action": "Apenas proprietários, gerentes e membros com acesso de gerenciamento podem realizar essa ação.",
"option_id": "ID da opção",
"option_ids": "IDs da Opção",
"or": "ou",
"organization": "organização",
"organization_id": "ID da Organização",
@@ -1314,7 +1316,7 @@
"columns": "colunas",
"company": "empresa",
"company_logo": "Logo da empresa",
"completed_responses": "respostas completas",
"completed_responses": "respostas parciais ou completas.",
"concat": "Concatenar +",
"conditional_logic": "Lógica Condicional",
"confirm_default_language": "Confirmar idioma padrão",

View File

@@ -124,6 +124,7 @@
"add_action": "Adicionar ação",
"add_filter": "Adicionar filtro",
"add_logo": "Adicionar logótipo",
"add_member": "Adicionar membro",
"add_project": "Adicionar projeto",
"add_to_team": "Adicionar à equipa",
"all": "Todos",
@@ -280,6 +281,7 @@
"only_one_file_allowed": "Apenas um ficheiro é permitido",
"only_owners_managers_and_manage_access_members_can_perform_this_action": "Apenas proprietários e gestores podem realizar esta ação.",
"option_id": "ID de Opção",
"option_ids": "IDs de Opção",
"or": "ou",
"organization": "Organização",
"organization_id": "ID da Organização",
@@ -1314,7 +1316,7 @@
"columns": "Colunas",
"company": "Empresa",
"company_logo": "Logotipo da empresa",
"completed_responses": "respostas concluídas",
"completed_responses": "respostas parciais ou completas",
"concat": "Concatenar +",
"conditional_logic": "Lógica Condicional",
"confirm_default_language": "Confirmar idioma padrão",

View File

@@ -124,6 +124,7 @@
"add_action": "新增操作",
"add_filter": "新增篩選器",
"add_logo": "新增標誌",
"add_member": "新增成員",
"add_project": "新增專案",
"add_to_team": "新增至團隊",
"all": "全部",
@@ -280,6 +281,7 @@
"only_one_file_allowed": "僅允許一個檔案",
"only_owners_managers_and_manage_access_members_can_perform_this_action": "只有擁有者、管理員和管理存取權限的成員才能執行此操作。",
"option_id": "選項 ID",
"option_ids": "選項 IDs",
"or": "或",
"organization": "組織",
"organization_id": "組織 ID",
@@ -1314,7 +1316,7 @@
"columns": "欄位",
"company": "公司",
"company_logo": "公司標誌",
"completed_responses": "完成的回應。",
"completed_responses": "部分或完整答复。",
"concat": "串連 +",
"conditional_logic": "條件邏輯",
"confirm_default_language": "確認預設語言",

10
packages/ai/.eslintrc.cjs Normal file
View File

@@ -0,0 +1,10 @@
module.exports = {
extends: ["@formbricks/eslint-config/library.js"],
parserOptions: {
project: "tsconfig.json",
tsconfigRootDir: __dirname,
},
rules: {
"no-console": "off",
},
};

198
packages/ai/README.md Normal file
View File

@@ -0,0 +1,198 @@
# @formbricks/ai
A model-agnostic AI package for Formbricks, providing a unified interface for LLM operations across different providers.
## Features
- **Multi-Provider Support**: OpenAI and Anthropic models with easy switching
- **Type-Safe**: Full TypeScript support with schema validation
- **Environment-Based Configuration**: Automatic provider selection via environment variables
- **Structured Output**: Generate validated JSON objects from prompts using schemas
- **Helper Functions**: Built-in summarization and translation utilities
## Installation
This package is part of the Formbricks monorepo and is intended for internal use.
```bash
pnpm install @formbricks/ai
```
## Quick Start
### Environment Configuration
Set up your environment variables:
```bash
# Provider selection (defaults to "openai")
AI_PROVIDER=openai # or "anthropic"
# Model selection (uses sensible defaults if not specified)
AI_MODEL=gpt-4 # or "claude-3-sonnet-20240229"
# API Keys
OPENAI_API_KEY=your_openai_key
ANTHROPIC_API_KEY=your_anthropic_key
# Optional: Custom base URL
AI_BASE_URL=https://your-custom-endpoint.com
```
### Basic Usage
#### Text Generation
```typescript
import { generateText } from "@formbricks/ai";
const result = await generateText({
prompt: "Explain quantum computing in simple terms",
system: "You are a helpful science teacher",
temperature: 0.7,
maxTokens: 200,
});
console.log(result.text);
```
#### Structured Object Generation
```typescript
import { z } from "zod";
import { generateObject } from "@formbricks/ai";
const analysisSchema = z.object({
sentiment: z.enum(["positive", "negative", "neutral"]),
summary: z.string(),
keyTopics: z.array(z.string()),
confidence: z.number().min(0).max(1),
});
const result = await generateObject({
prompt: "Analyze this customer feedback: 'The product is amazing but delivery was slow'",
schema: analysisSchema,
temperature: 0.3,
});
console.log(result.object.sentiment); // Type-safe access
console.log(result.object.keyTopics);
```
#### Helper Functions
```typescript
import { summarizeText, translateText } from "@formbricks/ai";
// Summarization
const summary = await summarizeText(longText, 150);
// Translation
const translated = await translateText("Hello, how are you?", "Spanish", "English");
```
## Configuration
### Programmatic Configuration
You can override environment configuration programmatically:
```typescript
import { createAIModel, generateText } from "@formbricks/ai";
const customConfig = {
provider: "anthropic" as const,
model: "claude-3-haiku-20240307",
apiKey: "your-api-key",
};
// Use custom config for specific calls
const result = await generateText(
{
prompt: "Hello world",
},
customConfig
);
// Or create a reusable model instance
const aiModel = createAIModel(customConfig);
```
### Supported Models
#### OpenAI
- `gpt-4` (default)
- `gpt-4-turbo`
- `gpt-3.5-turbo`
#### Anthropic
- `claude-3-sonnet-20240229` (default)
- `claude-3-haiku-20240307`
- `claude-3-opus-20240229`
## Error Handling
The package provides clear error messages for common issues:
```typescript
import { generateText, isAIConfigured } from "@formbricks/ai";
// Check if AI is properly configured
if (!isAIConfigured()) {
throw new Error("AI is not properly configured. Please check your environment variables.");
}
try {
const result = await generateText({
prompt: "Your prompt here",
});
} catch (error) {
console.error("AI generation failed:", error.message);
}
```
## Usage in Formbricks
This package is designed to be used across the Formbricks ecosystem:
- **NextJS API Routes**: For server-side AI operations
- **Background Jobs**: For processing surveys and responses
- **Future NestJS Backend**: Modular design allows easy integration
## Development
### Building
```bash
pnpm build
```
### Development Mode
```bash
pnpm dev
```
### Code Quality
```bash
pnpm lint
```
## Architecture
The package follows a layered architecture:
1. **Types Layer** (`types.ts`): TypeScript definitions and interfaces
2. **Configuration Layer** (`config.ts`): Provider setup and validation
3. **Abstraction Layer** (`ai.ts`): Main API functions
4. **Export Layer** (`index.ts`): Public API exports
This design ensures:
- Easy testing and mocking
- Provider-agnostic implementation
- Type safety throughout
- Consistent error handling

39
packages/ai/package.json Normal file
View File

@@ -0,0 +1,39 @@
{
"name": "@formbricks/ai",
"packageManager": "pnpm@9.15.9",
"private": true,
"version": "0.1.0",
"main": "./dist/index.cjs",
"types": "./dist/index.d.ts",
"type": "module",
"files": [
"dist"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"scripts": {
"clean": "rimraf .turbo node_modules dist",
"build": "vite build",
"dev": "vite build --watch",
"lint": "eslint ./src --fix",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@ai-sdk/anthropic": "^1.0.6",
"@ai-sdk/openai": "^1.0.20",
"ai": "^5.0.2",
"zod": "^3.25.76"
},
"devDependencies": {
"@formbricks/config-typescript": "workspace:*",
"@formbricks/eslint-config": "workspace:*",
"typescript": "5.8.3",
"vite": "6.3.5",
"vite-plugin-dts": "4.5.3"
}
}

196
packages/ai/src/ai.ts Normal file
View File

@@ -0,0 +1,196 @@
import { generateObject as aiGenerateObject, generateText as aiGenerateText } from "ai";
import type { z } from "zod";
import { createAIModel } from "./config";
import type {
GenerateObjectOptions,
GenerateObjectResult,
GenerateTextOptions,
GenerateTextResult,
ProviderConfig,
} from "./types";
/**
* Singleton AI model instance for reuse across calls
*/
let aiModelInstance: ReturnType<typeof createAIModel> | null = null;
/**
* Get or create the AI model instance
*/
function getAIModel(customConfig?: ProviderConfig) {
if (!aiModelInstance || customConfig) {
aiModelInstance = createAIModel(customConfig);
}
return aiModelInstance;
}
/**
* Generate text using the configured AI model
*
* @param options - Text generation options
* @returns Promise resolving to generated text and usage information
*
* @example
* ```typescript
* const result = await generateText({
* prompt: "Summarize the following text: Lorem ipsum...",
* system: "You are a helpful assistant that provides concise summaries.",
* temperature: 0.7,
* maxTokens: 150
* });
*
* console.log(result.text);
* ```
*/
export async function generateText(
options: GenerateTextOptions,
customConfig?: ProviderConfig
): Promise<GenerateTextResult> {
const { model } = getAIModel(customConfig);
try {
const result = await aiGenerateText({
model,
prompt: options.prompt,
system: options.system,
temperature: options.temperature,
...(options.maxTokens && { maxTokens: options.maxTokens }),
});
return {
text: result.text,
usage: result.usage
? {
inputTokens: result.usage.inputTokens,
outputTokens: result.usage.outputTokens,
totalTokens: result.usage.totalTokens,
}
: undefined,
};
} catch (error) {
throw new Error(`Failed to generate text: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Generate a structured object using the configured AI model
*
* @param options - Object generation options including Zod schema
* @returns Promise resolving to generated object and usage information
*
* @example
* ```typescript
* import { z } from "zod";
*
* const summarySchema = z.object({
* title: z.string(),
* summary: z.string(),
* keyPoints: z.array(z.string()),
* sentiment: z.enum(['positive', 'negative', 'neutral'])
* });
*
* const result = await generateObject({
* prompt: "Analyze the following article: Lorem ipsum...",
* schema: summarySchema,
* system: "You are an expert content analyzer.",
* temperature: 0.3
* });
*
* console.log(result.object.title);
* console.log(result.object.keyPoints);
* ```
*/
export async function generateObject<T extends z.ZodSchema>(
options: GenerateObjectOptions<T>,
customConfig?: ProviderConfig
): Promise<GenerateObjectResult<z.infer<T>>> {
const { model } = getAIModel(customConfig);
try {
const result = await aiGenerateObject({
model,
prompt: options.prompt,
schema: options.schema,
system: options.system,
temperature: options.temperature,
...(options.maxTokens && { maxTokens: options.maxTokens }),
});
return {
object: result.object,
usage: result.usage
? {
inputTokens: result.usage.inputTokens,
outputTokens: result.usage.outputTokens,
totalTokens: result.usage.totalTokens,
}
: undefined,
};
} catch (error) {
throw new Error(`Failed to generate object: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Helper function for text summarization
*
* @param text - Text to summarize
* @param maxLength - Optional maximum length for the summary
* @returns Promise resolving to the summary
*/
export async function summarizeText(
text: string,
maxLength?: number,
customConfig?: ProviderConfig
): Promise<string> {
const prompt = `Summarize the following text${maxLength ? ` in approximately ${maxLength} characters` : ""}:\n\n${text}`;
const result = await generateText(
{
prompt,
system:
"You are a helpful assistant that creates clear, concise summaries. Focus on the key points and main ideas.",
temperature: 0.3,
maxTokens: maxLength ? Math.ceil(maxLength / 3) : undefined, // Rough token estimate
},
customConfig
);
return result.text;
}
/**
* Helper function for text translation
*
* @param text - Text to translate
* @param targetLanguage - Target language for translation
* @param sourceLanguage - Optional source language (auto-detected if not provided)
* @returns Promise resolving to the translated text
*/
export async function translateText(
text: string,
targetLanguage: string,
sourceLanguage?: string,
customConfig?: ProviderConfig
): Promise<string> {
const sourceText = sourceLanguage ? `from ${sourceLanguage} ` : "";
const prompt = `Translate the following text ${sourceText}to ${targetLanguage}:\n\n${text}`;
const result = await generateText(
{
prompt,
system: `You are a professional translator. Provide only the translated text without any additional commentary or explanations. Maintain the original tone and style.`,
temperature: 0.1, // Low temperature for consistency
},
customConfig
);
return result.text;
}
/**
* Reset the AI model instance (useful for testing or when configuration changes)
*/
export function resetAIModel(): void {
aiModelInstance = null;
}

168
packages/ai/src/config.ts Normal file
View File

@@ -0,0 +1,168 @@
import { anthropic } from "@ai-sdk/anthropic";
import { openai } from "@ai-sdk/openai";
import type { LanguageModelV1 } from "ai";
import type {
AIEnvironmentConfig,
AIModelInstance,
AIProvider,
AnthropicConfig,
OpenAIConfig,
ProviderConfig,
} from "./types";
/**
* Default models for each provider
*/
const DEFAULT_MODELS: Record<AIProvider, string> = {
openai: "gpt-4",
anthropic: "claude-3-sonnet-20240229",
};
/**
* Get environment configuration from process.env
*/
function getEnvironmentConfig(): AIEnvironmentConfig {
return {
AI_PROVIDER: process.env.AI_PROVIDER,
AI_MODEL: process.env.AI_MODEL,
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
AI_BASE_URL: process.env.AI_BASE_URL,
};
}
/**
* Create provider configuration from environment variables
*/
function createProviderConfigFromEnv(): ProviderConfig {
const env = getEnvironmentConfig();
// Determine provider (default to openai if not specified)
const provider = (env.AI_PROVIDER as AIProvider) || "openai";
// Get model for the provider
const model = env.AI_MODEL || DEFAULT_MODELS[provider];
// Create configuration based on provider
switch (provider) {
case "openai": {
const config: OpenAIConfig = {
provider: "openai",
model,
apiKey: env.OPENAI_API_KEY,
baseURL: env.AI_BASE_URL,
};
return config;
}
case "anthropic": {
const config: AnthropicConfig = {
provider: "anthropic",
model,
apiKey: env.ANTHROPIC_API_KEY,
baseURL: env.AI_BASE_URL,
};
return config;
}
default:
throw new Error(`Unsupported AI provider: ${provider}`);
}
}
/**
* Create a language model instance from provider configuration
*/
function createModelFromConfig(config: ProviderConfig): LanguageModelV1 {
switch (config.provider) {
case "openai": {
const options: any = {};
if (config.apiKey) {
options.apiKey = config.apiKey;
}
if (config.baseURL) {
options.baseURL = config.baseURL;
}
return openai(config.model, options);
}
case "anthropic": {
const options: any = {};
if (config.apiKey) {
options.apiKey = config.apiKey;
}
if (config.baseURL) {
options.baseURL = config.baseURL;
}
return anthropic(config.model, options);
}
default:
throw new Error(`Unsupported provider: ${(config as ProviderConfig).provider}`);
}
}
/**
* Validate that required API keys are present for the configured provider
*/
function validateConfiguration(config: ProviderConfig): void {
switch (config.provider) {
case "openai":
if (!config.apiKey && !process.env.OPENAI_API_KEY) {
throw new Error(
"OpenAI API key is required. Set OPENAI_API_KEY environment variable or provide apiKey in configuration."
);
}
break;
case "anthropic":
if (!config.apiKey && !process.env.ANTHROPIC_API_KEY) {
throw new Error(
"Anthropic API key is required. Set ANTHROPIC_API_KEY environment variable or provide apiKey in configuration."
);
}
break;
default:
throw new Error(`Unsupported provider: ${(config as ProviderConfig).provider}`);
}
}
/**
* Create and configure the AI model instance
*/
export function createAIModel(customConfig?: ProviderConfig): AIModelInstance {
// Use custom config or create from environment
const config = customConfig || createProviderConfigFromEnv();
// Validate the configuration
validateConfiguration(config);
// Create the model instance
const model = createModelFromConfig(config);
return {
model,
config,
};
}
/**
* Get the current provider configuration without creating a model
*/
export function getProviderConfig(): ProviderConfig {
return createProviderConfigFromEnv();
}
/**
* Check if AI is properly configured
*/
export function isAIConfigured(): boolean {
try {
const config = createProviderConfigFromEnv();
validateConfiguration(config);
return true;
} catch {
return false;
}
}

20
packages/ai/src/index.ts Normal file
View File

@@ -0,0 +1,20 @@
// Main AI functions
export { generateText, generateObject, summarizeText, translateText, resetAIModel } from "./ai";
// Configuration functions
export { createAIModel, getProviderConfig, isAIConfigured } from "./config";
// Types
export type {
AIProvider,
AIProviderConfig,
OpenAIConfig,
AnthropicConfig,
ProviderConfig,
AIEnvironmentConfig,
GenerateTextOptions,
GenerateObjectOptions,
GenerateTextResult,
GenerateObjectResult,
AIModelInstance,
} from "./types";

102
packages/ai/src/types.ts Normal file
View File

@@ -0,0 +1,102 @@
import type { LanguageModelV1 } from "ai";
import type { z } from "zod";
/**
* Supported AI providers
*/
export type AIProvider = "openai" | "anthropic";
/**
* Configuration for different AI providers
*/
export interface AIProviderConfig {
provider: AIProvider;
model: string;
apiKey?: string;
baseURL?: string;
}
/**
* OpenAI specific configuration
*/
export interface OpenAIConfig extends AIProviderConfig {
provider: "openai";
model: string; // e.g., "gpt-4", "gpt-3.5-turbo"
}
/**
* Anthropic specific configuration
*/
export interface AnthropicConfig extends AIProviderConfig {
provider: "anthropic";
model: string; // e.g., "claude-3-sonnet-20240229", "claude-3-haiku-20240307"
}
/**
* Union type for all provider configurations
*/
export type ProviderConfig = OpenAIConfig | AnthropicConfig;
/**
* Environment variables for AI configuration
*/
export interface AIEnvironmentConfig {
AI_PROVIDER?: string;
AI_MODEL?: string;
OPENAI_API_KEY?: string;
ANTHROPIC_API_KEY?: string;
AI_BASE_URL?: string;
}
/**
* Options for text generation
*/
export interface GenerateTextOptions {
prompt: string;
system?: string;
temperature?: number;
maxTokens?: number;
}
/**
* Options for object generation
*/
export interface GenerateObjectOptions<T extends z.ZodSchema> {
prompt: string;
schema: T;
system?: string;
temperature?: number;
maxTokens?: number;
}
/**
* Result from text generation
*/
export interface GenerateTextResult {
text: string;
usage?: {
inputTokens?: number;
outputTokens?: number;
totalTokens?: number;
};
}
/**
* Result from object generation
*/
export interface GenerateObjectResult<T> {
object: T;
usage?: {
inputTokens?: number;
outputTokens?: number;
totalTokens?: number;
};
}
/**
* Internal type for the language model instance
*/
export interface AIModelInstance {
model: LanguageModelV1;
config: ProviderConfig;
}

12
packages/ai/tsconfig.json Normal file
View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "dist",
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"],
"extends": "@formbricks/config-typescript/js-library.json",
"include": ["src/**/*"]
}

View File

@@ -0,0 +1,49 @@
import { resolve } from "path";
import { UserConfig, defineConfig } from "vite";
import dts from "vite-plugin-dts";
export default defineConfig((): UserConfig => {
return {
resolve: {
alias: {
"@": resolve(__dirname, "src"),
},
},
build: {
rollupOptions: {
input: {
index: resolve(__dirname, "src/index.ts"),
},
output: [
{
format: "esm",
entryFileNames: "[name].js",
chunkFileNames: "[name].js",
},
{
format: "cjs",
entryFileNames: "[name].cjs",
chunkFileNames: "[name].cjs",
},
],
external: [
// External dependencies that should not be bundled
"@ai-sdk/anthropic",
"@ai-sdk/openai",
"ai",
"zod",
],
},
emptyOutDir: true,
ssr: true, // Server-side rendering mode for Node.js
},
plugins: [
dts({
rollupTypes: false,
include: ["src/**/*"],
exclude: ["src/**/*.test.ts", "src/**/*.spec.ts"],
insertTypesEntry: true,
}),
],
};
});

149
pnpm-lock.yaml generated
View File

@@ -536,6 +536,37 @@ importers:
specifier: 3.1.0
version: 3.1.0(typescript@5.8.3)(vitest@3.1.3(@types/node@22.15.18)(jiti@2.4.2)(jsdom@26.1.0)(terser@5.39.1)(tsx@4.19.4)(yaml@2.8.0))
packages/ai:
dependencies:
'@ai-sdk/anthropic':
specifier: ^1.0.6
version: 1.2.12(zod@3.24.4)
'@ai-sdk/openai':
specifier: ^1.0.20
version: 1.3.23(zod@3.24.4)
ai:
specifier: ^5.0.2
version: 5.0.2(zod@3.24.4)
zod:
specifier: 3.24.4
version: 3.24.4
devDependencies:
'@formbricks/config-typescript':
specifier: workspace:*
version: link:../config-typescript
'@formbricks/eslint-config':
specifier: workspace:*
version: link:../config-eslint
typescript:
specifier: 5.8.3
version: 5.8.3
vite:
specifier: 6.3.5
version: 6.3.5(@types/node@22.15.18)(jiti@2.4.2)(terser@5.39.1)(tsx@4.19.4)(yaml@2.8.0)
vite-plugin-dts:
specifier: 4.5.3
version: 4.5.3(@types/node@22.15.18)(rollup@4.46.1)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.18)(jiti@2.4.2)(terser@5.39.1)(tsx@4.19.4)(yaml@2.8.0))
packages/config-eslint:
devDependencies:
'@next/eslint-plugin-next':
@@ -824,6 +855,44 @@ packages:
'@adobe/css-tools@4.4.3':
resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==}
'@ai-sdk/anthropic@1.2.12':
resolution: {integrity: sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
'@ai-sdk/gateway@1.0.0':
resolution: {integrity: sha512-VEm87DyRx1yIPywbTy8ntoyh4jEDv1rJ88m+2I7zOm08jJI5BhFtAWh0OF6YzZu1Vu4NxhOWO4ssGdsqydDQ3A==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
'@ai-sdk/openai@1.3.23':
resolution: {integrity: sha512-86U7rFp8yacUAOE/Jz8WbGcwMCqWvjK33wk5DXkfnAOEn3mx2r7tNSJdjukQFZbAK97VMXGPPHxF+aEARDXRXQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
'@ai-sdk/provider-utils@2.2.8':
resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.23.8
'@ai-sdk/provider-utils@3.0.0':
resolution: {integrity: sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
'@ai-sdk/provider@1.1.3':
resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==}
engines: {node: '>=18'}
'@ai-sdk/provider@2.0.0':
resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==}
engines: {node: '>=18'}
'@alloc/quick-lru@5.2.0':
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
@@ -3734,6 +3803,9 @@ packages:
'@sqltools/formatter@1.2.5':
resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
'@standard-schema/spec@1.0.0':
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
'@standard-schema/utils@0.3.0':
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
@@ -4677,6 +4749,12 @@ packages:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
ai@5.0.2:
resolution: {integrity: sha512-Uk4lmwlr2b/4G9DUYCWYKcWz93xQ6p6AEeRZN+/AO9NbOyCm9axrDru26c83Ax8OB8IHUvoseA3CqaZkg9Z0Kg==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
ajv-draft-04@1.0.0:
resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
peerDependencies:
@@ -5887,6 +5965,10 @@ packages:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
eventsource-parser@3.0.3:
resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==}
engines: {node: '>=20.0.0'}
expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
@@ -6723,6 +6805,9 @@ packages:
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
json-schema@0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
@@ -9490,6 +9575,11 @@ packages:
peerDependencies:
zod: ^3.21.4
zod-to-json-schema@3.24.6:
resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==}
peerDependencies:
zod: ^3.24.1
zod@3.24.4:
resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==}
@@ -9497,6 +9587,47 @@ snapshots:
'@adobe/css-tools@4.4.3': {}
'@ai-sdk/anthropic@1.2.12(zod@3.24.4)':
dependencies:
'@ai-sdk/provider': 1.1.3
'@ai-sdk/provider-utils': 2.2.8(zod@3.24.4)
zod: 3.24.4
'@ai-sdk/gateway@1.0.0(zod@3.24.4)':
dependencies:
'@ai-sdk/provider': 2.0.0
'@ai-sdk/provider-utils': 3.0.0(zod@3.24.4)
zod: 3.24.4
'@ai-sdk/openai@1.3.23(zod@3.24.4)':
dependencies:
'@ai-sdk/provider': 1.1.3
'@ai-sdk/provider-utils': 2.2.8(zod@3.24.4)
zod: 3.24.4
'@ai-sdk/provider-utils@2.2.8(zod@3.24.4)':
dependencies:
'@ai-sdk/provider': 1.1.3
nanoid: 3.3.11
secure-json-parse: 2.7.0
zod: 3.24.4
'@ai-sdk/provider-utils@3.0.0(zod@3.24.4)':
dependencies:
'@ai-sdk/provider': 2.0.0
'@standard-schema/spec': 1.0.0
eventsource-parser: 3.0.3
zod: 3.24.4
zod-to-json-schema: 3.24.6(zod@3.24.4)
'@ai-sdk/provider@1.1.3':
dependencies:
json-schema: 0.4.0
'@ai-sdk/provider@2.0.0':
dependencies:
json-schema: 0.4.0
'@alloc/quick-lru@5.2.0': {}
'@ampproject/remapping@2.3.0':
@@ -13506,6 +13637,8 @@ snapshots:
'@sqltools/formatter@1.2.5': {}
'@standard-schema/spec@1.0.0': {}
'@standard-schema/utils@0.3.0': {}
'@storybook/addon-a11y@9.0.15(storybook@9.0.15(@testing-library/dom@8.20.1)(prettier@3.5.3))':
@@ -14624,6 +14757,14 @@ snapshots:
indent-string: 4.0.0
optional: true
ai@5.0.2(zod@3.24.4):
dependencies:
'@ai-sdk/gateway': 1.0.0(zod@3.24.4)
'@ai-sdk/provider': 2.0.0
'@ai-sdk/provider-utils': 3.0.0(zod@3.24.4)
'@opentelemetry/api': 1.9.0
zod: 3.24.4
ajv-draft-04@1.0.0(ajv@8.13.0):
optionalDependencies:
ajv: 8.13.0
@@ -16043,6 +16184,8 @@ snapshots:
events@3.3.0: {}
eventsource-parser@3.0.3: {}
expand-template@2.0.3: {}
expect-type@1.2.2: {}
@@ -16926,6 +17069,8 @@ snapshots:
json-schema-traverse@1.0.0: {}
json-schema@0.4.0: {}
json-stable-stringify-without-jsonify@1.0.1: {}
json5@1.0.2:
@@ -19879,4 +20024,8 @@ snapshots:
dependencies:
zod: 3.24.4
zod-to-json-schema@3.24.6(zod@3.24.4):
dependencies:
zod: 3.24.4
zod@3.24.4: {}