@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,158 @@
1
+ // @geenius-ai/react — src/hooks/useAI.ts
2
+
3
+ /**
4
+ * Core AI hook — generate text/image/audio/video with loading state.
5
+ */
6
+
7
+ import { useState, useCallback } from 'react'
8
+ import { useAction } from 'convex/react'
9
+ import type { AIGenerationType } from '@geenius-ai/shared'
10
+
11
+ // Stable no-op placeholder for optional Convex action references.
12
+ // Ensures hooks are always called unconditionally (React Rules of Hooks).
13
+ const _noop = (() => Promise.resolve(null)) as any
14
+
15
+ export interface UseAIOptions {
16
+ /** Convex action reference for text generation (api.ai.generateText) */
17
+ generateTextAction?: any
18
+ /** Convex action reference for image generation */
19
+ generateImageAction?: any
20
+ /** Convex action reference for audio generation */
21
+ generateAudioAction?: any
22
+ /** Convex action reference for transcription */
23
+ transcribeAudioAction?: any
24
+ /** Convex action reference for video generation */
25
+ generateVideoAction?: any
26
+ }
27
+
28
+ export interface UseAIReturn {
29
+ generateText: (args: {
30
+ model: string
31
+ messages: any[]
32
+ temperature?: number
33
+ maxTokens?: number
34
+ caller?: string
35
+ }) => Promise<string>
36
+ generateImage: (prompt: string, model?: string) => Promise<string>
37
+ generateAudio: (prompt: string, voice?: string) => Promise<string>
38
+ transcribeAudio: (audio: string) => Promise<string>
39
+ generateVideo: (prompt: string) => Promise<string>
40
+ isLoading: boolean
41
+ error: string | null
42
+ lastResult: string | null
43
+ lastType: AIGenerationType | null
44
+ clearError: () => void
45
+ }
46
+
47
+ export function useAI(options: UseAIOptions = {}): UseAIReturn {
48
+ const [isLoading, setIsLoading] = useState(false)
49
+ const [error, setError] = useState<string | null>(null)
50
+ const [lastResult, setLastResult] = useState<string | null>(null)
51
+ const [lastType, setLastType] = useState<AIGenerationType | null>(null)
52
+
53
+ // Always call hooks unconditionally; gate actual invocations on options presence
54
+ const textAction = useAction(options.generateTextAction ?? _noop)
55
+ const imageAction = useAction(options.generateImageAction ?? _noop)
56
+ const audioAction = useAction(options.generateAudioAction ?? _noop)
57
+ const transcribeAction = useAction(options.transcribeAudioAction ?? _noop)
58
+ const videoAction = useAction(options.generateVideoAction ?? _noop)
59
+
60
+ const generateText = useCallback(async (args: {
61
+ model: string; messages: any[]; temperature?: number; maxTokens?: number; caller?: string
62
+ }) => {
63
+ if (!options.generateTextAction) throw new Error('generateTextAction not provided')
64
+ setIsLoading(true)
65
+ setError(null)
66
+ try {
67
+ const result = await textAction(args)
68
+ setLastResult(result)
69
+ setLastType('text')
70
+ return result
71
+ } catch (err) {
72
+ const msg = err instanceof Error ? err.message : 'Text generation failed'
73
+ setError(msg)
74
+ throw err
75
+ } finally {
76
+ setIsLoading(false)
77
+ }
78
+ }, [textAction])
79
+
80
+ const generateImage = useCallback(async (prompt: string, model?: string) => {
81
+ if (!options.generateImageAction) throw new Error('generateImageAction not provided')
82
+ setIsLoading(true)
83
+ setError(null)
84
+ try {
85
+ const result = await imageAction({ prompt, model })
86
+ setLastResult(result)
87
+ setLastType('image')
88
+ return result
89
+ } catch (err) {
90
+ const msg = err instanceof Error ? err.message : 'Image generation failed'
91
+ setError(msg)
92
+ throw err
93
+ } finally {
94
+ setIsLoading(false)
95
+ }
96
+ }, [imageAction])
97
+
98
+ const generateAudio = useCallback(async (prompt: string, voice?: string) => {
99
+ if (!options.generateAudioAction) throw new Error('generateAudioAction not provided')
100
+ setIsLoading(true)
101
+ setError(null)
102
+ try {
103
+ const result = await audioAction({ prompt, voice })
104
+ setLastResult(result)
105
+ setLastType('audio')
106
+ return result
107
+ } catch (err) {
108
+ const msg = err instanceof Error ? err.message : 'Audio generation failed'
109
+ setError(msg)
110
+ throw err
111
+ } finally {
112
+ setIsLoading(false)
113
+ }
114
+ }, [audioAction])
115
+
116
+ const transcribeAudio = useCallback(async (audio: string) => {
117
+ if (!options.transcribeAudioAction) throw new Error('transcribeAudioAction not provided')
118
+ setIsLoading(true)
119
+ setError(null)
120
+ try {
121
+ const result = await transcribeAction({ audio })
122
+ setLastResult(result)
123
+ setLastType('transcription')
124
+ return result
125
+ } catch (err) {
126
+ const msg = err instanceof Error ? err.message : 'Transcription failed'
127
+ setError(msg)
128
+ throw err
129
+ } finally {
130
+ setIsLoading(false)
131
+ }
132
+ }, [transcribeAction])
133
+
134
+ const generateVideo = useCallback(async (prompt: string) => {
135
+ if (!options.generateVideoAction) throw new Error('generateVideoAction not provided')
136
+ setIsLoading(true)
137
+ setError(null)
138
+ try {
139
+ const result = await videoAction({ prompt })
140
+ setLastResult(result)
141
+ setLastType('video')
142
+ return result
143
+ } catch (err) {
144
+ const msg = err instanceof Error ? err.message : 'Video generation failed'
145
+ setError(msg)
146
+ throw err
147
+ } finally {
148
+ setIsLoading(false)
149
+ }
150
+ }, [videoAction])
151
+
152
+ const clearError = useCallback(() => setError(null), [])
153
+
154
+ return {
155
+ generateText, generateImage, generateAudio, transcribeAudio, generateVideo,
156
+ isLoading, error, lastResult, lastType, clearError,
157
+ }
158
+ }
@@ -0,0 +1,40 @@
1
+ // @geenius-ai/react — src/hooks/useAILogs.ts
2
+
3
+ /**
4
+ * Hook for querying AI logs with filters.
5
+ */
6
+
7
+ import { useQuery } from 'convex/react'
8
+ import type { AILogEntry, AIRequestStatus } from '@geenius-ai/shared'
9
+
10
+ export interface UseAILogsOptions {
11
+ /** Query reference: api.ai.listLogs */
12
+ listLogsQuery: any
13
+ filters?: {
14
+ model?: string
15
+ provider?: string
16
+ status?: AIRequestStatus
17
+ caller?: string
18
+ }
19
+ limit?: number
20
+ }
21
+
22
+ export interface UseAILogsReturn {
23
+ logs: AILogEntry[]
24
+ isLoading: boolean
25
+ }
26
+
27
+ export function useAILogs(options: UseAILogsOptions): UseAILogsReturn {
28
+ const logs = useQuery(options.listLogsQuery, {
29
+ model: options.filters?.model,
30
+ provider: options.filters?.provider,
31
+ status: options.filters?.status,
32
+ caller: options.filters?.caller,
33
+ limit: options.limit ?? 50,
34
+ })
35
+
36
+ return {
37
+ logs: (logs ?? []) as AILogEntry[],
38
+ isLoading: logs === undefined,
39
+ }
40
+ }
@@ -0,0 +1,53 @@
1
+ // @geenius-ai/react — src/hooks/useAIModels.ts
2
+
3
+ /**
4
+ * Hook for managing AI models and providers.
5
+ */
6
+
7
+ import { useQuery, useMutation } from 'convex/react'
8
+ import { useCallback } from 'react'
9
+ import type { AIModel } from '@geenius-ai/shared'
10
+
11
+ // Stable no-op for optional Convex mutation references (Rules of Hooks)
12
+ const _noop = (() => Promise.resolve(null)) as any
13
+
14
+ export interface UseAIModelsOptions {
15
+ /** Query reference: api.ai.listModels */
16
+ listModelsQuery: any
17
+ /** Mutation reference: api.ai.upsertModel */
18
+ upsertModelMutation?: any
19
+ }
20
+
21
+ export interface UseAIModelsReturn {
22
+ models: AIModel[]
23
+ isLoading: boolean
24
+ upsertModel: (model: Partial<AIModel> & { id: string; provider: string }) => Promise<void>
25
+ }
26
+
27
+ export function useAIModels(options: UseAIModelsOptions): UseAIModelsReturn {
28
+ const models = useQuery(options.listModelsQuery, {})
29
+ // Always call useMutation unconditionally (React Rules of Hooks)
30
+ const upsertMutation = useMutation(options.upsertModelMutation ?? _noop)
31
+
32
+ const upsertModel = useCallback(async (
33
+ model: Partial<AIModel> & { id: string; provider: string },
34
+ ) => {
35
+ if (!options.upsertModelMutation) throw new Error('upsertModelMutation not provided')
36
+ await upsertMutation({
37
+ model: model.id,
38
+ provider: model.provider,
39
+ displayName: model.displayName,
40
+ inputCostPer1k: model.inputCostPer1k ?? 0,
41
+ outputCostPer1k: model.outputCostPer1k ?? 0,
42
+ capabilities: model.capabilities,
43
+ contextWindow: model.contextWindow,
44
+ isActive: model.isActive,
45
+ })
46
+ }, [upsertMutation, options.upsertModelMutation])
47
+
48
+ return {
49
+ models: (models ?? []) as AIModel[],
50
+ isLoading: models === undefined,
51
+ upsertModel,
52
+ }
53
+ }
@@ -0,0 +1,141 @@
1
+ // @geenius-ai/react — src/hooks/useChat.ts
2
+
3
+ /**
4
+ * Chat conversation hook — manages messages, sends messages,
5
+ * and tracks conversation state.
6
+ */
7
+
8
+ import { useState, useCallback, useRef, useEffect } from 'react'
9
+ import { useQuery, useMutation, useAction, skipToken } from 'convex/react'
10
+ import type { AIChatMessage, AIMessageRole } from '@geenius-ai/shared'
11
+
12
+ // Stable no-op for optional Convex mutation/action refs (React Rules of Hooks)
13
+ const _noop = (() => Promise.resolve(null)) as any
14
+
15
+ export interface UseChatOptions {
16
+ conversationId?: string
17
+ /** Query reference: api.ai.getConversation */
18
+ getConversationQuery?: any
19
+ /** Query reference: api.ai.listMessages */
20
+ listMessagesQuery?: any
21
+ /** Mutation reference: api.ai.sendMessage */
22
+ sendMessageMutation?: any
23
+ /** Action reference: api.ai.generateText (for AI response) */
24
+ generateTextAction?: any
25
+ /** Mutation reference: api.ai.createConversation */
26
+ createConversationMutation?: any
27
+ model?: string
28
+ systemPrompt?: string
29
+ onNewConversation?: (id: string) => void
30
+ }
31
+
32
+ export interface UseChatReturn {
33
+ messages: AIChatMessage[]
34
+ isLoading: boolean
35
+ isSending: boolean
36
+ error: string | null
37
+ sendMessage: (content: string) => Promise<void>
38
+ conversationId: string | null
39
+ clearError: () => void
40
+ }
41
+
42
+ export function useChat(options: UseChatOptions = {}): UseChatReturn {
43
+ const [localMessages, setLocalMessages] = useState<AIChatMessage[]>([])
44
+ const [isSending, setIsSending] = useState(false)
45
+ const [error, setError] = useState<string | null>(null)
46
+ const [conversationId, setConversationId] = useState<string | null>(options.conversationId ?? null)
47
+
48
+ // Always call Convex hooks unconditionally (React Rules of Hooks).
49
+ // skipToken skips the query when no ref or no conversationId is available.
50
+ const convexMessages = useQuery(
51
+ options.listMessagesQuery ?? skipToken,
52
+ options.listMessagesQuery && conversationId ? { conversationId } : skipToken,
53
+ )
54
+ const sendMutation = useMutation(options.sendMessageMutation ?? _noop)
55
+ const createConversation = useMutation(options.createConversationMutation ?? _noop)
56
+ const textAction = useAction(options.generateTextAction ?? _noop)
57
+
58
+ const messages = convexMessages ?? localMessages
59
+
60
+ const sendMessage = useCallback(async (content: string) => {
61
+ setIsSending(true)
62
+ setError(null)
63
+
64
+ try {
65
+ let activeConversationId = conversationId
66
+
67
+ // Create conversation if needed
68
+ if (!activeConversationId && options.createConversationMutation) {
69
+ activeConversationId = await createConversation({
70
+ title: content.substring(0, 100),
71
+ model: options.model ?? 'gpt-4o',
72
+ systemPrompt: options.systemPrompt,
73
+ })
74
+ setConversationId(activeConversationId)
75
+ if (activeConversationId) options.onNewConversation?.(activeConversationId)
76
+ }
77
+
78
+ // Add user message
79
+ if (options.sendMessageMutation && activeConversationId) {
80
+ await sendMutation({ conversationId: activeConversationId, content })
81
+ } else {
82
+ // Local-only mode
83
+ const userMsg: AIChatMessage = {
84
+ id: `msg-${Date.now()}`,
85
+ conversationId: activeConversationId ?? 'local',
86
+ userId: 'local',
87
+ role: 'user',
88
+ content,
89
+ createdAt: Date.now(),
90
+ }
91
+ setLocalMessages(prev => [...prev, userMsg])
92
+ }
93
+
94
+ // Generate AI response if action provided
95
+ if (options.generateTextAction) {
96
+ const aiResponse = await textAction({
97
+ model: options.model ?? 'gpt-4o',
98
+ messages: [
99
+ ...(options.systemPrompt
100
+ ? [{ role: 'system', content: options.systemPrompt }]
101
+ : []),
102
+ ...(messages as AIChatMessage[]).map((m: AIChatMessage) => ({ role: m.role, content: m.content })),
103
+ { role: 'user', content },
104
+ ],
105
+ caller: 'chat',
106
+ })
107
+
108
+ // If local-only, add the response too
109
+ if (!options.sendMessageMutation) {
110
+ const assistantMsg: AIChatMessage = {
111
+ id: `msg-${Date.now()}-ai`,
112
+ conversationId: activeConversationId ?? 'local',
113
+ userId: 'ai',
114
+ role: 'assistant',
115
+ content: aiResponse,
116
+ model: options.model ?? 'gpt-4o',
117
+ createdAt: Date.now(),
118
+ }
119
+ setLocalMessages(prev => [...prev, assistantMsg])
120
+ }
121
+ }
122
+ } catch (err) {
123
+ const msg = err instanceof Error ? err.message : 'Failed to send message'
124
+ setError(msg)
125
+ } finally {
126
+ setIsSending(false)
127
+ }
128
+ }, [conversationId, sendMutation, createConversation, textAction, messages, options])
129
+
130
+ const clearError = useCallback(() => setError(null), [])
131
+
132
+ return {
133
+ messages: messages as AIChatMessage[],
134
+ isLoading: convexMessages === undefined && !!options.listMessagesQuery,
135
+ isSending,
136
+ error,
137
+ sendMessage,
138
+ conversationId,
139
+ clearError,
140
+ }
141
+ }
@@ -0,0 +1,108 @@
1
+ // @geenius-ai/react — src/hooks/useContentManager.ts
2
+
3
+ import { useState, useCallback } from 'react'
4
+ import type {
5
+ ContentAction,
6
+ ContentTone,
7
+ ContentType,
8
+ ContentGenerateOptions,
9
+ ContentResult,
10
+ ContentTemplate,
11
+ } from '@geenius-ai/shared'
12
+
13
+ export interface UseContentManagerOptions {
14
+ /** Convex action for content generation */
15
+ generateFn: (options: ContentGenerateOptions) => Promise<ContentResult>
16
+ /** Default content type */
17
+ defaultType?: ContentType
18
+ /** Default tone */
19
+ defaultTone?: ContentTone
20
+ /** Default model */
21
+ defaultModel?: string
22
+ }
23
+
24
+ export interface UseContentManagerReturn {
25
+ /** Generate content */
26
+ generate: (input: string, options?: Partial<ContentGenerateOptions>) => Promise<ContentResult>
27
+ /** Rewrite content */
28
+ rewrite: (text: string, instructions?: string) => Promise<ContentResult>
29
+ /** Translate content */
30
+ translate: (text: string, language: string) => Promise<ContentResult>
31
+ /** Summarize content */
32
+ summarize: (text: string) => Promise<ContentResult>
33
+ /** Generate variations */
34
+ variations: (text: string, count?: number) => Promise<ContentResult>
35
+ /** Change tone */
36
+ changeTone: (text: string, tone: ContentTone) => Promise<ContentResult>
37
+ /** Proofread */
38
+ proofread: (text: string) => Promise<ContentResult>
39
+ /** Current result */
40
+ result: ContentResult | null
41
+ /** Loading state */
42
+ isGenerating: boolean
43
+ /** Error state */
44
+ error: Error | null
45
+ /** Clear state */
46
+ reset: () => void
47
+ }
48
+
49
+ export function useContentManager(options: UseContentManagerOptions): UseContentManagerReturn {
50
+ const { generateFn, defaultType = 'text', defaultTone, defaultModel } = options
51
+ const [result, setResult] = useState<ContentResult | null>(null)
52
+ const [isGenerating, setIsGenerating] = useState(false)
53
+ const [error, setError] = useState<Error | null>(null)
54
+
55
+ const execute = useCallback(async (action: ContentAction, input: string, extra?: Partial<ContentGenerateOptions>) => {
56
+ setIsGenerating(true)
57
+ setError(null)
58
+ try {
59
+ const res = await generateFn({
60
+ action,
61
+ input,
62
+ type: extra?.type ?? defaultType,
63
+ tone: extra?.tone ?? defaultTone,
64
+ model: extra?.model ?? defaultModel,
65
+ ...extra,
66
+ })
67
+ setResult(res)
68
+ return res
69
+ } catch (err) {
70
+ const e = err instanceof Error ? err : new Error(String(err))
71
+ setError(e)
72
+ throw e
73
+ } finally {
74
+ setIsGenerating(false)
75
+ }
76
+ }, [generateFn, defaultType, defaultTone, defaultModel])
77
+
78
+ const generate = useCallback((input: string, opts?: Partial<ContentGenerateOptions>) =>
79
+ execute('generate', input, opts), [execute])
80
+
81
+ const rewrite = useCallback((text: string, instructions?: string) =>
82
+ execute('rewrite', text, { instructions }), [execute])
83
+
84
+ const translate = useCallback((text: string, language: string) =>
85
+ execute('translate', text, { language }), [execute])
86
+
87
+ const summarize = useCallback((text: string) =>
88
+ execute('summarize', text), [execute])
89
+
90
+ const variations = useCallback((text: string, count: number = 3) =>
91
+ execute('variations', text, { variations: count }), [execute])
92
+
93
+ const changeTone = useCallback((text: string, tone: ContentTone) =>
94
+ execute('change-tone', text, { tone }), [execute])
95
+
96
+ const proofread = useCallback((text: string) =>
97
+ execute('proofread', text), [execute])
98
+
99
+ const reset = useCallback(() => {
100
+ setResult(null)
101
+ setError(null)
102
+ }, [])
103
+
104
+ return {
105
+ generate, rewrite, translate, summarize, variations, changeTone, proofread,
106
+ result, isGenerating, error, reset,
107
+ }
108
+ }
@@ -0,0 +1,82 @@
1
+ // @geenius-ai/react — src/hooks/useImageGeneration.ts
2
+
3
+ /**
4
+ * Image generation hook (DALL-E, SDXL, Flux, etc.)
5
+ */
6
+
7
+ import { useState, useCallback } from 'react'
8
+ import { useAction } from 'convex/react'
9
+
10
+ export interface GeneratedImage {
11
+ url: string
12
+ prompt: string
13
+ model: string
14
+ timestamp: number
15
+ }
16
+
17
+ export interface UseImageGenerationOptions {
18
+ generateImageAction: any
19
+ defaultModel?: string
20
+ }
21
+
22
+ export interface UseImageGenerationReturn {
23
+ generate: (prompt: string, options?: {
24
+ model?: string
25
+ negativePrompt?: string
26
+ size?: string
27
+ quality?: 'standard' | 'hd'
28
+ style?: 'natural' | 'vivid'
29
+ n?: number
30
+ seed?: number
31
+ }) => Promise<string>
32
+ images: GeneratedImage[]
33
+ isGenerating: boolean
34
+ error: string | null
35
+ clearImages: () => void
36
+ clearError: () => void
37
+ }
38
+
39
+ export function useImageGeneration(options: UseImageGenerationOptions): UseImageGenerationReturn {
40
+ const [images, setImages] = useState<GeneratedImage[]>([])
41
+ const [isGenerating, setIsGenerating] = useState(false)
42
+ const [error, setError] = useState<string | null>(null)
43
+ const action = useAction(options.generateImageAction)
44
+
45
+ const generate = useCallback(async (prompt: string, opts?: {
46
+ model?: string; negativePrompt?: string; size?: string
47
+ quality?: 'standard' | 'hd'; style?: 'natural' | 'vivid'; n?: number; seed?: number
48
+ }) => {
49
+ setIsGenerating(true)
50
+ setError(null)
51
+ try {
52
+ const model = opts?.model ?? options.defaultModel ?? 'dall-e-3'
53
+ const result = await action({
54
+ prompt, model,
55
+ negativePrompt: opts?.negativePrompt,
56
+ size: opts?.size ?? '1024x1024',
57
+ quality: opts?.quality,
58
+ style: opts?.style,
59
+ n: opts?.n ?? 1,
60
+ seed: opts?.seed,
61
+ })
62
+ const img: GeneratedImage = {
63
+ url: result.startsWith('http') ? result : `data:image/png;base64,${result}`,
64
+ prompt, model, timestamp: Date.now(),
65
+ }
66
+ setImages(prev => [img, ...prev])
67
+ return img.url
68
+ } catch (err) {
69
+ const msg = err instanceof Error ? err.message : 'Image generation failed'
70
+ setError(msg)
71
+ throw err
72
+ } finally {
73
+ setIsGenerating(false)
74
+ }
75
+ }, [action, options.defaultModel])
76
+
77
+ return {
78
+ generate, images, isGenerating, error,
79
+ clearImages: () => setImages([]),
80
+ clearError: () => setError(null),
81
+ }
82
+ }