@geenius/ai 0.1.0

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.
Files changed (165) hide show
  1. package/.changeset/config.json +11 -0
  2. package/.env.example +2 -0
  3. package/.github/CODEOWNERS +1 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
  6. package/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  7. package/.github/dependabot.yml +11 -0
  8. package/.github/workflows/ci.yml +23 -0
  9. package/.github/workflows/release.yml +29 -0
  10. package/.node-version +1 -0
  11. package/.nvmrc +1 -0
  12. package/.prettierrc +7 -0
  13. package/.project/ACCOUNT.yaml +4 -0
  14. package/.project/IDEAS.yaml +7 -0
  15. package/.project/PROJECT.yaml +11 -0
  16. package/.project/ROADMAP.yaml +15 -0
  17. package/CHANGELOG.md +15 -0
  18. package/CODE_OF_CONDUCT.md +26 -0
  19. package/CONTRIBUTING.md +61 -0
  20. package/LICENSE +21 -0
  21. package/README.md +1 -0
  22. package/SECURITY.md +18 -0
  23. package/SUPPORT.md +14 -0
  24. package/package.json +75 -0
  25. package/packages/convex/package.json +42 -0
  26. package/packages/convex/src/index.ts +8 -0
  27. package/packages/convex/src/mutations/messages.ts +29 -0
  28. package/packages/convex/src/queries/messages.ts +24 -0
  29. package/packages/convex/src/schema.ts +20 -0
  30. package/packages/convex/tsconfig.json +11 -0
  31. package/packages/convex/tsup.config.ts +17 -0
  32. package/packages/react/README.md +1 -0
  33. package/packages/react/package.json +60 -0
  34. package/packages/react/src/components/AILogTable.tsx +90 -0
  35. package/packages/react/src/components/ChatWindow.tsx +118 -0
  36. package/packages/react/src/components/GenerationCard.tsx +73 -0
  37. package/packages/react/src/components/ImageGenerator.tsx +103 -0
  38. package/packages/react/src/components/ModelSelector.tsx +44 -0
  39. package/packages/react/src/components/ModelTestRunner.tsx +148 -0
  40. package/packages/react/src/components/VoiceSelector.tsx +51 -0
  41. package/packages/react/src/components/index.ts +9 -0
  42. package/packages/react/src/hooks/index.ts +12 -0
  43. package/packages/react/src/hooks/useAI.ts +158 -0
  44. package/packages/react/src/hooks/useAILogs.ts +40 -0
  45. package/packages/react/src/hooks/useAIModels.ts +53 -0
  46. package/packages/react/src/hooks/useChat.ts +141 -0
  47. package/packages/react/src/hooks/useContentManager.ts +108 -0
  48. package/packages/react/src/hooks/useImageGeneration.ts +82 -0
  49. package/packages/react/src/hooks/useMemory.ts +161 -0
  50. package/packages/react/src/hooks/useModelTest.ts +126 -0
  51. package/packages/react/src/hooks/useRealtimeAudio.ts +203 -0
  52. package/packages/react/src/hooks/useSkills.ts +114 -0
  53. package/packages/react/src/hooks/useTextToSpeech.ts +99 -0
  54. package/packages/react/src/hooks/useTranscription.ts +119 -0
  55. package/packages/react/src/hooks/useVideoGeneration.ts +79 -0
  56. package/packages/react/src/index.ts +42 -0
  57. package/packages/react/src/pages/AILogsPage.tsx +98 -0
  58. package/packages/react/src/pages/ChatPage.tsx +42 -0
  59. package/packages/react/src/pages/ModelTestPage.tsx +33 -0
  60. package/packages/react/src/pages/index.ts +5 -0
  61. package/packages/react/tsconfig.json +26 -0
  62. package/packages/react/tsup.config.ts +22 -0
  63. package/packages/react-css/README.md +1 -0
  64. package/packages/react-css/package.json +45 -0
  65. package/packages/react-css/src/ai.css +857 -0
  66. package/packages/react-css/src/components/AILogTable.tsx +90 -0
  67. package/packages/react-css/src/components/ChatWindow.tsx +118 -0
  68. package/packages/react-css/src/components/GenerationCard.tsx +73 -0
  69. package/packages/react-css/src/components/ImageGenerator.tsx +103 -0
  70. package/packages/react-css/src/components/ModelSelector.tsx +44 -0
  71. package/packages/react-css/src/components/ModelTestRunner.tsx +148 -0
  72. package/packages/react-css/src/components/VoiceSelector.tsx +51 -0
  73. package/packages/react-css/src/components/index.ts +9 -0
  74. package/packages/react-css/src/hooks/index.ts +12 -0
  75. package/packages/react-css/src/hooks/useAI.ts +153 -0
  76. package/packages/react-css/src/hooks/useAILogs.ts +40 -0
  77. package/packages/react-css/src/hooks/useAIModels.ts +51 -0
  78. package/packages/react-css/src/hooks/useChat.ts +145 -0
  79. package/packages/react-css/src/hooks/useContentManager.ts +108 -0
  80. package/packages/react-css/src/hooks/useImageGeneration.ts +82 -0
  81. package/packages/react-css/src/hooks/useMemory.ts +161 -0
  82. package/packages/react-css/src/hooks/useModelTest.ts +122 -0
  83. package/packages/react-css/src/hooks/useRealtimeAudio.ts +203 -0
  84. package/packages/react-css/src/hooks/useSkills.ts +114 -0
  85. package/packages/react-css/src/hooks/useTextToSpeech.ts +99 -0
  86. package/packages/react-css/src/hooks/useTranscription.ts +119 -0
  87. package/packages/react-css/src/hooks/useVideoGeneration.ts +79 -0
  88. package/packages/react-css/src/index.ts +35 -0
  89. package/packages/react-css/src/pages/AILogsPage.tsx +98 -0
  90. package/packages/react-css/src/pages/ChatPage.tsx +42 -0
  91. package/packages/react-css/src/pages/ModelTestPage.tsx +33 -0
  92. package/packages/react-css/src/pages/index.ts +5 -0
  93. package/packages/react-css/src/styles.css +127 -0
  94. package/packages/react-css/tsconfig.json +26 -0
  95. package/packages/react-css/tsup.config.ts +2 -0
  96. package/packages/shared/README.md +1 -0
  97. package/packages/shared/package.json +71 -0
  98. package/packages/shared/src/__tests__/ai.test.ts +67 -0
  99. package/packages/shared/src/ai-client.ts +243 -0
  100. package/packages/shared/src/config.ts +235 -0
  101. package/packages/shared/src/content.ts +249 -0
  102. package/packages/shared/src/convex/helpers.ts +163 -0
  103. package/packages/shared/src/convex/index.ts +16 -0
  104. package/packages/shared/src/convex/schemas.ts +146 -0
  105. package/packages/shared/src/convex/validators.ts +136 -0
  106. package/packages/shared/src/index.ts +107 -0
  107. package/packages/shared/src/memory.ts +197 -0
  108. package/packages/shared/src/providers/base.ts +103 -0
  109. package/packages/shared/src/providers/elevenlabs.ts +155 -0
  110. package/packages/shared/src/providers/index.ts +28 -0
  111. package/packages/shared/src/providers/openai-compatible.ts +286 -0
  112. package/packages/shared/src/providers/registry.ts +113 -0
  113. package/packages/shared/src/providers/replicate-fal.ts +230 -0
  114. package/packages/shared/src/skills.ts +273 -0
  115. package/packages/shared/src/types.ts +501 -0
  116. package/packages/shared/tsconfig.json +25 -0
  117. package/packages/shared/tsup.config.ts +22 -0
  118. package/packages/shared/vitest.config.ts +4 -0
  119. package/packages/solidjs/README.md +1 -0
  120. package/packages/solidjs/package.json +59 -0
  121. package/packages/solidjs/src/components/ChatWindow.tsx +78 -0
  122. package/packages/solidjs/src/components/GenerationCard.tsx +62 -0
  123. package/packages/solidjs/src/components/ModelTestRunner.tsx +119 -0
  124. package/packages/solidjs/src/components/index.ts +5 -0
  125. package/packages/solidjs/src/index.ts +32 -0
  126. package/packages/solidjs/src/pages/ChatPage.tsx +22 -0
  127. package/packages/solidjs/src/pages/ModelTestPage.tsx +22 -0
  128. package/packages/solidjs/src/pages/index.ts +4 -0
  129. package/packages/solidjs/src/primitives/createAI.ts +79 -0
  130. package/packages/solidjs/src/primitives/createChat.ts +100 -0
  131. package/packages/solidjs/src/primitives/createContentManager.ts +61 -0
  132. package/packages/solidjs/src/primitives/createImageGeneration.ts +46 -0
  133. package/packages/solidjs/src/primitives/createMemory.ts +127 -0
  134. package/packages/solidjs/src/primitives/createModelTest.ts +89 -0
  135. package/packages/solidjs/src/primitives/createSkills.ts +83 -0
  136. package/packages/solidjs/src/primitives/createTextToSpeech.ts +56 -0
  137. package/packages/solidjs/src/primitives/createVideoGeneration.ts +46 -0
  138. package/packages/solidjs/src/primitives/index.ts +8 -0
  139. package/packages/solidjs/tsconfig.json +27 -0
  140. package/packages/solidjs/tsup.config.ts +21 -0
  141. package/packages/solidjs-css/README.md +1 -0
  142. package/packages/solidjs-css/package.json +44 -0
  143. package/packages/solidjs-css/src/ai.css +857 -0
  144. package/packages/solidjs-css/src/components/ChatWindow.tsx +78 -0
  145. package/packages/solidjs-css/src/components/GenerationCard.tsx +62 -0
  146. package/packages/solidjs-css/src/components/ModelTestRunner.tsx +119 -0
  147. package/packages/solidjs-css/src/components/index.ts +5 -0
  148. package/packages/solidjs-css/src/index.ts +26 -0
  149. package/packages/solidjs-css/src/pages/ChatPage.tsx +22 -0
  150. package/packages/solidjs-css/src/pages/ModelTestPage.tsx +22 -0
  151. package/packages/solidjs-css/src/pages/index.ts +4 -0
  152. package/packages/solidjs-css/src/primitives/createAI.ts +79 -0
  153. package/packages/solidjs-css/src/primitives/createChat.ts +100 -0
  154. package/packages/solidjs-css/src/primitives/createContentManager.ts +61 -0
  155. package/packages/solidjs-css/src/primitives/createImageGeneration.ts +46 -0
  156. package/packages/solidjs-css/src/primitives/createMemory.ts +127 -0
  157. package/packages/solidjs-css/src/primitives/createModelTest.ts +89 -0
  158. package/packages/solidjs-css/src/primitives/createSkills.ts +83 -0
  159. package/packages/solidjs-css/src/primitives/createTextToSpeech.ts +56 -0
  160. package/packages/solidjs-css/src/primitives/createVideoGeneration.ts +46 -0
  161. package/packages/solidjs-css/src/primitives/index.ts +1 -0
  162. package/packages/solidjs-css/src/styles.css +127 -0
  163. package/packages/solidjs-css/tsconfig.json +27 -0
  164. package/packages/solidjs-css/tsup.config.ts +2 -0
  165. package/pnpm-workspace.yaml +2 -0
@@ -0,0 +1,136 @@
1
+ // @geenius-ai/shared — src/convex/validators.ts
2
+
3
+ /**
4
+ * Convex validators for AI actions, queries, and mutations.
5
+ */
6
+
7
+ import { v } from 'convex/values'
8
+
9
+ // ============================================================================
10
+ // Action Validators
11
+ // ============================================================================
12
+
13
+ export const generateTextArgs = {
14
+ model: v.string(),
15
+ messages: v.any(), // AIMessage[]
16
+ temperature: v.optional(v.number()),
17
+ maxTokens: v.optional(v.number()),
18
+ topP: v.optional(v.number()),
19
+ caller: v.optional(v.string()),
20
+ responseFormat: v.optional(v.any()),
21
+ }
22
+
23
+ export const generateImageArgs = {
24
+ model: v.optional(v.string()),
25
+ prompt: v.string(),
26
+ n: v.optional(v.number()),
27
+ size: v.optional(v.string()),
28
+ }
29
+
30
+ export const generateAudioArgs = {
31
+ model: v.optional(v.string()),
32
+ prompt: v.string(),
33
+ voice: v.optional(v.string()),
34
+ speed: v.optional(v.number()),
35
+ }
36
+
37
+ export const transcribeAudioArgs = {
38
+ model: v.optional(v.string()),
39
+ audio: v.string(),
40
+ language: v.optional(v.string()),
41
+ }
42
+
43
+ export const generateVideoArgs = {
44
+ model: v.optional(v.string()),
45
+ prompt: v.string(),
46
+ duration: v.optional(v.number()),
47
+ }
48
+
49
+ export const testModelArgs = {
50
+ model: v.string(),
51
+ prompt: v.string(),
52
+ temperature: v.optional(v.number()),
53
+ maxTokens: v.optional(v.number()),
54
+ }
55
+
56
+ // ============================================================================
57
+ // Log Validators
58
+ // ============================================================================
59
+
60
+ export const saveLogArgs = {
61
+ requestId: v.string(),
62
+ model: v.string(),
63
+ provider: v.string(),
64
+ caller: v.string(),
65
+ type: v.string(),
66
+ timestamp: v.number(),
67
+ durationMs: v.number(),
68
+ systemPrompt: v.string(),
69
+ userPrompt: v.string(),
70
+ hasImage: v.boolean(),
71
+ imageSizeBytes: v.optional(v.number()),
72
+ temperature: v.optional(v.number()),
73
+ maxTokens: v.optional(v.number()),
74
+ requestBodySize: v.number(),
75
+ status: v.union(v.literal('success'), v.literal('error')),
76
+ httpStatus: v.number(),
77
+ responseContent: v.string(),
78
+ responseSize: v.number(),
79
+ finishReason: v.optional(v.string()),
80
+ promptTokens: v.optional(v.number()),
81
+ completionTokens: v.optional(v.number()),
82
+ totalTokens: v.optional(v.number()),
83
+ errorMessage: v.optional(v.string()),
84
+ inputCostUsd: v.optional(v.number()),
85
+ outputCostUsd: v.optional(v.number()),
86
+ totalCostUsd: v.optional(v.number()),
87
+ }
88
+
89
+ export const listLogsArgs = {
90
+ model: v.optional(v.string()),
91
+ provider: v.optional(v.string()),
92
+ status: v.optional(v.union(v.literal('success'), v.literal('error'))),
93
+ caller: v.optional(v.string()),
94
+ limit: v.optional(v.number()),
95
+ }
96
+
97
+ // ============================================================================
98
+ // Model Cost Validators
99
+ // ============================================================================
100
+
101
+ export const upsertModelArgs = {
102
+ model: v.string(),
103
+ provider: v.string(),
104
+ displayName: v.optional(v.string()),
105
+ inputCostPer1k: v.number(),
106
+ outputCostPer1k: v.number(),
107
+ capabilities: v.optional(v.array(v.string())),
108
+ contextWindow: v.optional(v.number()),
109
+ isActive: v.optional(v.boolean()),
110
+ }
111
+
112
+ // ============================================================================
113
+ // Conversation Validators
114
+ // ============================================================================
115
+
116
+ export const createConversationArgs = {
117
+ title: v.string(),
118
+ model: v.string(),
119
+ systemPrompt: v.optional(v.string()),
120
+ tags: v.optional(v.array(v.string())),
121
+ }
122
+
123
+ export const updateConversationArgs = {
124
+ conversationId: v.id('aiConversations'),
125
+ title: v.optional(v.string()),
126
+ model: v.optional(v.string()),
127
+ systemPrompt: v.optional(v.string()),
128
+ tags: v.optional(v.array(v.string())),
129
+ favorite: v.optional(v.boolean()),
130
+ status: v.optional(v.union(v.literal('active'), v.literal('archived'))),
131
+ }
132
+
133
+ export const sendMessageArgs = {
134
+ conversationId: v.id('aiConversations'),
135
+ content: v.string(),
136
+ }
@@ -0,0 +1,107 @@
1
+ // @geenius-ai/shared — src/index.ts
2
+
3
+ // Types
4
+ export type {
5
+ AIProviderType,
6
+ AIGenerationType,
7
+ AIModelCapability,
8
+ AIModel,
9
+ AIProviderConfig,
10
+ AIMessageRole,
11
+ AIMessageContentPart,
12
+ AIMessage,
13
+ AIToolCall,
14
+ AIToolDefinition,
15
+ AIConversation,
16
+ AIConversationStatus,
17
+ AIChatMessage,
18
+ AIAttachment,
19
+ AIGenerateTextOptions,
20
+ AIStructuredOutputOptions,
21
+ AIGenerateImageOptions,
22
+ AIEditImageOptions,
23
+ AIGenerateAudioOptions,
24
+ AITranscribeOptions,
25
+ AIGenerateVideoOptions,
26
+ AIEmbeddingOptions,
27
+ AIGenerateMusicOptions,
28
+ AIGenerateSoundEffectOptions,
29
+ AIVoiceCloneOptions,
30
+ AIGenerationResult,
31
+ AIEmbeddingResult,
32
+ AITranscriptionResult,
33
+ AITranscriptionSegment,
34
+ AITranscriptionWord,
35
+ AIRealtimeConfig,
36
+ AIRealtimeEvent,
37
+ AILogEntry,
38
+ AIRequestStatus,
39
+ AIUsageStats,
40
+ AIConfig,
41
+ ChatWindowProps,
42
+ ModelTestPageProps,
43
+ AILogTableProps,
44
+ ImageGeneratorProps,
45
+ AudioPlayerProps,
46
+ VoiceSelectorProps,
47
+ AIVoiceOption,
48
+ RealtimeAudioProps,
49
+ } from './types'
50
+
51
+ // Config
52
+ export {
53
+ defineAIConfig,
54
+ mergeAIConfig,
55
+ findProviderConfig,
56
+ findModelConfig,
57
+ getProviderForModel,
58
+ getModelsByCapability,
59
+ DEFAULT_PROVIDERS,
60
+ DEFAULT_MODELS,
61
+ DEFAULT_VOICES,
62
+ } from './config'
63
+
64
+ // Content
65
+ export type {
66
+ ContentType,
67
+ ContentAction,
68
+ ContentTone,
69
+ ContentTemplate,
70
+ ContentOutputField,
71
+ ContentItem,
72
+ ContentSchema,
73
+ ContentFieldMeta,
74
+ ContentGenerateOptions,
75
+ ContentResult,
76
+ } from './content'
77
+ export { BUILT_IN_TEMPLATES, buildContentPrompt, buildActionSystemPrompt } from './content'
78
+
79
+ // Memory
80
+ export type {
81
+ MemoryNamespace,
82
+ MemoryType,
83
+ MemoryImportance,
84
+ MemoryEntry,
85
+ MemoryQuery,
86
+ MemoryStore,
87
+ UserPreference,
88
+ OrgPattern,
89
+ MemoryConfig,
90
+ } from './memory'
91
+ export { DEFAULT_MEMORY_CONFIG, buildMemoryContext, extractPreferenceHints } from './memory'
92
+
93
+ // Skills
94
+ export type {
95
+ SkillCategory,
96
+ SkillParameterType,
97
+ SkillParameter,
98
+ SkillDefinition,
99
+ SkillContext,
100
+ SkillResult,
101
+ SkillRegistry,
102
+ } from './skills'
103
+ export { BUILT_IN_SKILLS, createSkillRegistry, buildSkillPrompt } from './skills'
104
+
105
+ // Lightweight AI Client (fetch-based, no provider registry required)
106
+ export type { AIProvider, AIClientConfig, AIClientMessage, ToolCall, ToolDefinition, StreamCallbacks, AIResponse } from './ai-client'
107
+ export { resolveAIProvider, AIClient, createAI } from './ai-client'
@@ -0,0 +1,197 @@
1
+ // @geenius-ai/shared — src/memory.ts
2
+
3
+ /**
4
+ * AI memory system — persistent, contextual memory for AI interactions.
5
+ * Supports user-level preferences, org-level patterns, and conversation memory.
6
+ */
7
+
8
+ import type { AIProviderType } from './types'
9
+
10
+ // ============================================================================
11
+ // Memory Types
12
+ // ============================================================================
13
+
14
+ export type MemoryNamespace = 'user' | 'org' | 'conversation' | 'global'
15
+ export type MemoryType = 'preference' | 'pattern' | 'fact' | 'interaction' | 'feedback' | 'context'
16
+ export type MemoryImportance = 'low' | 'medium' | 'high' | 'critical'
17
+
18
+ export interface MemoryEntry {
19
+ id: string
20
+ namespace: MemoryNamespace
21
+ type: MemoryType
22
+ importance: MemoryImportance
23
+ /** The key identifying this memory (e.g., "preferred_tone", "date_format") */
24
+ key: string
25
+ /** The stored value */
26
+ value: string
27
+ /** Structured metadata */
28
+ metadata?: Record<string, unknown>
29
+ /** Embedding vector for semantic search */
30
+ embedding?: number[]
31
+ /** Namespace-scoped ID (userId, orgId, conversationId) */
32
+ scopeId: string
33
+ /** How many times this memory has been accessed */
34
+ accessCount: number
35
+ /** Last time this memory was accessed */
36
+ lastAccessedAt: number
37
+ /** When this memory expires (0 = never) */
38
+ expiresAt?: number
39
+ createdAt: number
40
+ updatedAt: number
41
+ }
42
+
43
+ export interface MemoryQuery {
44
+ namespace?: MemoryNamespace
45
+ type?: MemoryType
46
+ key?: string
47
+ scopeId?: string
48
+ /** Semantic search query */
49
+ query?: string
50
+ /** Min importance level */
51
+ minImportance?: MemoryImportance
52
+ /** Max results */
53
+ limit?: number
54
+ /** Include expired entries */
55
+ includeExpired?: boolean
56
+ }
57
+
58
+ export interface MemoryStore {
59
+ /** Store a new memory entry */
60
+ set(entry: Omit<MemoryEntry, 'id' | 'accessCount' | 'lastAccessedAt' | 'createdAt' | 'updatedAt'>): Promise<MemoryEntry>
61
+ /** Get a specific memory by key and scope */
62
+ get(namespace: MemoryNamespace, key: string, scopeId: string): Promise<MemoryEntry | null>
63
+ /** Search memories */
64
+ search(query: MemoryQuery): Promise<MemoryEntry[]>
65
+ /** Delete a memory */
66
+ delete(id: string): Promise<void>
67
+ /** Clear all memories in a namespace/scope */
68
+ clear(namespace: MemoryNamespace, scopeId: string): Promise<void>
69
+ }
70
+
71
+ // ============================================================================
72
+ // User Preferences & Org Patterns
73
+ // ============================================================================
74
+
75
+ export interface UserPreference {
76
+ key: string
77
+ value: string
78
+ /** How confident we are in this preference (0-1) */
79
+ confidence: number
80
+ /** Number of times observed */
81
+ occurrences: number
82
+ /** Source of the preference */
83
+ source: 'explicit' | 'inferred' | 'default'
84
+ }
85
+
86
+ export interface OrgPattern {
87
+ key: string
88
+ value: string
89
+ /** How many users in the org follow this pattern */
90
+ adoptionRate: number
91
+ /** Description of the pattern */
92
+ description?: string
93
+ examples?: string[]
94
+ }
95
+
96
+ // ============================================================================
97
+ // Memory Config
98
+ // ============================================================================
99
+
100
+ export interface MemoryConfig {
101
+ /** Enable memory system */
102
+ enabled: boolean
103
+ /** Max memories per namespace */
104
+ maxPerNamespace: number
105
+ /** Auto-expire memories after N days (0 = never) */
106
+ autoExpireDays: number
107
+ /** Enable semantic search (requires embedding model) */
108
+ semanticSearch: boolean
109
+ /** Embedding model to use */
110
+ embeddingModel?: string
111
+ /** Namespaces to enable */
112
+ enabledNamespaces: MemoryNamespace[]
113
+ /** Auto-extract preferences from conversations */
114
+ autoExtract: boolean
115
+ /** Max context tokens to inject from memory */
116
+ maxContextTokens: number
117
+ }
118
+
119
+ export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
120
+ enabled: true,
121
+ maxPerNamespace: 1000,
122
+ autoExpireDays: 0,
123
+ semanticSearch: false,
124
+ embeddingModel: 'text-embedding-3-small',
125
+ enabledNamespaces: ['user', 'org', 'conversation'],
126
+ autoExtract: true,
127
+ maxContextTokens: 2000,
128
+ }
129
+
130
+ // ============================================================================
131
+ // Helpers
132
+ // ============================================================================
133
+
134
+ const IMPORTANCE_ORDER: Record<MemoryImportance, number> = {
135
+ low: 0,
136
+ medium: 1,
137
+ high: 2,
138
+ critical: 3,
139
+ }
140
+
141
+ /**
142
+ * Build a context string from relevant memories to inject into AI prompts.
143
+ */
144
+ export function buildMemoryContext(
145
+ memories: MemoryEntry[],
146
+ maxTokens: number = 2000,
147
+ ): string {
148
+ if (memories.length === 0) return ''
149
+
150
+ // Sort by importance (desc), then recency (desc)
151
+ const sorted = [...memories].sort((a, b) => {
152
+ const impDiff = IMPORTANCE_ORDER[b.importance] - IMPORTANCE_ORDER[a.importance]
153
+ if (impDiff !== 0) return impDiff
154
+ return b.lastAccessedAt - a.lastAccessedAt
155
+ })
156
+
157
+ const lines: string[] = ['<context_memory>']
158
+ let approxTokens = 4 // opening tag
159
+
160
+ for (const mem of sorted) {
161
+ const line = `- [${mem.type}] ${mem.key}: ${mem.value}`
162
+ const lineTokens = Math.ceil(line.length / 4) // rough token estimate
163
+ if (approxTokens + lineTokens > maxTokens) break
164
+ lines.push(line)
165
+ approxTokens += lineTokens
166
+ }
167
+
168
+ lines.push('</context_memory>')
169
+ return lines.join('\n')
170
+ }
171
+
172
+ /**
173
+ * Extract potential preferences from a conversation message.
174
+ */
175
+ export function extractPreferenceHints(text: string): Array<{ key: string; value: string }> {
176
+ const hints: Array<{ key: string; value: string }> = []
177
+
178
+ // Language preferences
179
+ const langMatch = text.match(/(?:in|to|use)\s+(english|french|german|spanish|dutch|italian|portuguese|japanese|chinese|korean|arabic)/i)
180
+ if (langMatch) {
181
+ hints.push({ key: 'preferred_language', value: langMatch[1].toLowerCase() })
182
+ }
183
+
184
+ // Tone preferences
185
+ const toneMatch = text.match(/(?:tone|style|voice).*?(?:be|is|use)\s+(professional|casual|friendly|formal|technical)/i)
186
+ if (toneMatch) {
187
+ hints.push({ key: 'preferred_tone', value: toneMatch[1].toLowerCase() })
188
+ }
189
+
190
+ // Currency preferences
191
+ const currencyMatch = text.match(/(?:in|use)\s+(EUR|USD|GBP|JPY|CHF|CAD|AUD)(?:\b)/i)
192
+ if (currencyMatch) {
193
+ hints.push({ key: 'preferred_currency', value: currencyMatch[1].toUpperCase() })
194
+ }
195
+
196
+ return hints
197
+ }
@@ -0,0 +1,103 @@
1
+ // @geenius-ai/shared — src/providers/base.ts
2
+
3
+ /**
4
+ * Base provider interface — all AI providers implement this.
5
+ */
6
+
7
+ import type {
8
+ AIProviderType,
9
+ AIGenerationResult,
10
+ AIEmbeddingResult,
11
+ AITranscriptionResult,
12
+ AIGenerateTextOptions,
13
+ AIStructuredOutputOptions,
14
+ AIGenerateImageOptions,
15
+ AIEditImageOptions,
16
+ AIGenerateAudioOptions,
17
+ AITranscribeOptions,
18
+ AIGenerateVideoOptions,
19
+ AIEmbeddingOptions,
20
+ AIGenerateMusicOptions,
21
+ AIGenerateSoundEffectOptions,
22
+ AIVoiceCloneOptions,
23
+ AIMessage,
24
+ } from '../types'
25
+
26
+ export interface AIProviderInterface {
27
+ type: AIProviderType
28
+ name: string
29
+
30
+ /** Generate text completion. */
31
+ generateText(options: AIGenerateTextOptions): Promise<AIGenerationResult>
32
+
33
+ /** Generate structured JSON output. */
34
+ generateStructuredOutput?(options: AIStructuredOutputOptions): Promise<AIGenerationResult>
35
+
36
+ /** Generate an image from a text prompt. */
37
+ generateImage?(options: AIGenerateImageOptions): Promise<string>
38
+
39
+ /** Edit an existing image. */
40
+ editImage?(options: AIEditImageOptions): Promise<string>
41
+
42
+ /** Generate speech/audio from text (TTS). Returns base64. */
43
+ generateAudio?(options: AIGenerateAudioOptions): Promise<string>
44
+
45
+ /** Transcribe audio to text (ASR). */
46
+ transcribeAudio?(options: AITranscribeOptions): Promise<string | AITranscriptionResult>
47
+
48
+ /** Generate video from text prompt. Returns URL. */
49
+ generateVideo?(options: AIGenerateVideoOptions): Promise<string>
50
+
51
+ /** Generate embeddings. */
52
+ generateEmbeddings?(options: AIEmbeddingOptions): Promise<AIEmbeddingResult>
53
+
54
+ /** Generate music from text. Returns base64. */
55
+ generateMusic?(options: AIGenerateMusicOptions): Promise<string>
56
+
57
+ /** Generate sound effects. Returns base64. */
58
+ generateSoundEffect?(options: AIGenerateSoundEffectOptions): Promise<string>
59
+
60
+ /** Clone a voice from samples. Returns voice ID. */
61
+ cloneVoice?(options: AIVoiceCloneOptions): Promise<string>
62
+
63
+ /** List available voices. */
64
+ listVoices?(): Promise<Array<{ id: string; name: string; preview?: string }>>
65
+ }
66
+
67
+ /**
68
+ * Extract text content from AIMessage array for logging.
69
+ */
70
+ export function extractPromptText(messages: AIMessage[]): {
71
+ systemPrompt: string
72
+ userPromptText: string
73
+ hasImage: boolean
74
+ imageSizeBytes: number
75
+ } {
76
+ let systemPrompt = ''
77
+ let userPromptText = ''
78
+ let hasImage = false
79
+ let imageSizeBytes = 0
80
+
81
+ for (const msg of messages) {
82
+ if (typeof msg.content === 'string') {
83
+ if (msg.role === 'system') systemPrompt = msg.content
84
+ else userPromptText += msg.content
85
+ } else if (Array.isArray(msg.content)) {
86
+ for (const part of msg.content) {
87
+ if (part.type === 'text' && part.text) {
88
+ if (msg.role === 'system') systemPrompt = part.text
89
+ else userPromptText += part.text
90
+ }
91
+ if (part.type === 'image_url' && part.image_url?.url) {
92
+ hasImage = true
93
+ imageSizeBytes = part.image_url.url.length
94
+ }
95
+ if (part.type === 'audio' && part.audio?.data) {
96
+ imageSizeBytes += part.audio.data.length
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ return { systemPrompt, userPromptText, hasImage, imageSizeBytes }
103
+ }
@@ -0,0 +1,155 @@
1
+ // @geenius-ai/shared — src/providers/elevenlabs.ts
2
+
3
+ /**
4
+ * ElevenLabs provider — dedicated TTS, voice cloning, and sound effects.
5
+ */
6
+
7
+ import type {
8
+ AIProviderType,
9
+ AIGenerationResult,
10
+ AIGenerateTextOptions,
11
+ AIGenerateAudioOptions,
12
+ AIVoiceCloneOptions,
13
+ AIGenerateSoundEffectOptions,
14
+ } from '../types'
15
+ import type { AIProviderInterface } from './base'
16
+
17
+ export interface ElevenLabsConfig {
18
+ apiKey: string
19
+ baseUrl?: string
20
+ defaultModel?: string
21
+ defaultVoice?: string
22
+ }
23
+
24
+ export class ElevenLabsProvider implements AIProviderInterface {
25
+ type: AIProviderType = 'elevenlabs'
26
+ name = 'ElevenLabs'
27
+ private apiKey: string
28
+ private baseUrl: string
29
+ private defaultModel: string
30
+ private defaultVoice: string
31
+
32
+ constructor(config: ElevenLabsConfig) {
33
+ this.apiKey = config.apiKey
34
+ this.baseUrl = (config.baseUrl ?? 'https://api.elevenlabs.io').replace(/\/$/, '')
35
+ this.defaultModel = config.defaultModel ?? 'eleven_multilingual_v2'
36
+ this.defaultVoice = config.defaultVoice ?? 'pNInz6obpgDQGcFmaJgB' // Adam
37
+ }
38
+
39
+ async generateText(_options: AIGenerateTextOptions): Promise<AIGenerationResult> {
40
+ throw new Error('ElevenLabs does not support text generation. Use generateAudio instead.')
41
+ }
42
+
43
+ async generateAudio(options: AIGenerateAudioOptions): Promise<string> {
44
+ const voiceId = options.voice ?? this.defaultVoice
45
+ const response = await fetch(
46
+ `${this.baseUrl}/v1/text-to-speech/${voiceId}`,
47
+ {
48
+ method: 'POST',
49
+ headers: {
50
+ 'Content-Type': 'application/json',
51
+ 'xi-api-key': this.apiKey,
52
+ 'Accept': 'audio/mpeg',
53
+ },
54
+ body: JSON.stringify({
55
+ model_id: options.model ?? this.defaultModel,
56
+ text: options.prompt,
57
+ voice_settings: {
58
+ stability: options.voiceSettings?.stability ?? 0.5,
59
+ similarity_boost: options.voiceSettings?.similarityBoost ?? 0.75,
60
+ style: options.voiceSettings?.style ?? 0.0,
61
+ use_speaker_boost: options.voiceSettings?.useSpeakerBoost ?? true,
62
+ },
63
+ }),
64
+ },
65
+ )
66
+
67
+ if (!response.ok) {
68
+ const errorText = await response.text()
69
+ throw new Error(`ElevenLabs TTS error (${response.status}): ${errorText}`)
70
+ }
71
+
72
+ const arrayBuffer = await response.arrayBuffer()
73
+ const bytes = new Uint8Array(arrayBuffer)
74
+ let binary = ''
75
+ for (let i = 0; i < bytes.length; i++) {
76
+ binary += String.fromCharCode(bytes[i]!)
77
+ }
78
+ return btoa(binary)
79
+ }
80
+
81
+ async cloneVoice(options: AIVoiceCloneOptions): Promise<string> {
82
+ const formData = new FormData()
83
+ formData.append('name', options.name)
84
+ if (options.description) formData.append('description', options.description)
85
+
86
+ for (let i = 0; i < options.samples.length; i++) {
87
+ const binaryStr = atob(options.samples[i]!)
88
+ const bytes = new Uint8Array(binaryStr.length)
89
+ for (let j = 0; j < binaryStr.length; j++) {
90
+ bytes[j] = binaryStr.charCodeAt(j)
91
+ }
92
+ const blob = new Blob([bytes], { type: 'audio/mp3' })
93
+ formData.append('files', blob, `sample_${i}.mp3`)
94
+ }
95
+
96
+ const response = await fetch(`${this.baseUrl}/v1/voices/add`, {
97
+ method: 'POST',
98
+ headers: { 'xi-api-key': this.apiKey },
99
+ body: formData,
100
+ })
101
+
102
+ if (!response.ok) {
103
+ const errorText = await response.text()
104
+ throw new Error(`ElevenLabs voice clone error (${response.status}): ${errorText}`)
105
+ }
106
+
107
+ const data = await response.json() as any
108
+ return data.voice_id
109
+ }
110
+
111
+ async generateSoundEffect(options: AIGenerateSoundEffectOptions): Promise<string> {
112
+ const response = await fetch(`${this.baseUrl}/v1/sound-generation`, {
113
+ method: 'POST',
114
+ headers: {
115
+ 'Content-Type': 'application/json',
116
+ 'xi-api-key': this.apiKey,
117
+ 'Accept': 'audio/mpeg',
118
+ },
119
+ body: JSON.stringify({
120
+ text: options.prompt,
121
+ duration_seconds: options.duration,
122
+ }),
123
+ })
124
+
125
+ if (!response.ok) {
126
+ const errorText = await response.text()
127
+ throw new Error(`ElevenLabs SFX error (${response.status}): ${errorText}`)
128
+ }
129
+
130
+ const arrayBuffer = await response.arrayBuffer()
131
+ const bytes = new Uint8Array(arrayBuffer)
132
+ let binary = ''
133
+ for (let i = 0; i < bytes.length; i++) {
134
+ binary += String.fromCharCode(bytes[i]!)
135
+ }
136
+ return btoa(binary)
137
+ }
138
+
139
+ async listVoices(): Promise<Array<{ id: string; name: string; preview?: string }>> {
140
+ const response = await fetch(`${this.baseUrl}/v1/voices`, {
141
+ headers: { 'xi-api-key': this.apiKey },
142
+ })
143
+
144
+ if (!response.ok) {
145
+ throw new Error(`ElevenLabs list voices error (${response.status})`)
146
+ }
147
+
148
+ const data = await response.json() as any
149
+ return (data.voices ?? []).map((v: any) => ({
150
+ id: v.voice_id,
151
+ name: v.name,
152
+ preview: v.preview_url,
153
+ }))
154
+ }
155
+ }