howone 0.1.9 → 0.1.11
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.
package/package.json
CHANGED
|
@@ -5,16 +5,85 @@ description: Build application code with @howone/sdk correctly. Use when impleme
|
|
|
5
5
|
|
|
6
6
|
# HowOne SDK
|
|
7
7
|
|
|
8
|
-
Use this skill when writing app code that consumes `@howone/sdk`. The goal is stable generated code: read backend-synced manifests, define
|
|
8
|
+
Use this skill when writing app code that consumes `@howone/sdk`. The goal is stable generated code: read backend-synced manifests, define runtime schemas, bind entities and AI actions in the app SDK entry, then call those bindings from UI code.
|
|
9
|
+
|
|
10
|
+
This skill is the normal source of truth for HowOne SDK usage in generated apps. Do not inspect `node_modules/@howone/sdk` just to rediscover imports, hook names, entity methods, AI action call shapes, or Vite environment wiring. Use dependency source only for narrow missing details, signature drift proven by verification, or SDK-package development.
|
|
9
11
|
|
|
10
12
|
## Workflow
|
|
11
13
|
|
|
12
|
-
1.
|
|
13
|
-
2. Read
|
|
14
|
-
3.
|
|
15
|
-
4.
|
|
16
|
-
5.
|
|
17
|
-
6.
|
|
14
|
+
1. Establish the app root. All `.howone/*` metadata and `src/lib/sdk.ts` work belongs inside that app root.
|
|
15
|
+
2. Read `node_modules/@howone/sdk/dist/index.d.ts` and `node_modules/@howone/sdk/dist/react.d.ts` to get the complete, always-up-to-date type signatures and public exports.
|
|
16
|
+
3. Read `references/usage-patterns.md` for generation recipes, usage examples, and known traps that aren't in the type declarations.
|
|
17
|
+
4. Read the existing app SDK entry, usually `src/lib/sdk.ts`, before editing. Preserve its imports and local style.
|
|
18
|
+
5. For Entity bindings, read `.howone/database/manifest.json`.
|
|
19
|
+
6. For AI action bindings, read `.howone/ai/manifest.json`.
|
|
20
|
+
7. Generate source code from the manifests, not from memory or tool-call summaries.
|
|
21
|
+
8. Keep sync tools and app-code edits separate. `sync_schema_artifacts` and `sync_ai_artifacts` write metadata only; the coding agent edits `src/lib/sdk.ts`.
|
|
22
|
+
|
|
23
|
+
## Quick SDK Contract
|
|
24
|
+
|
|
25
|
+
See `/sdk/dist/index.d.ts` and `/sdk/dist/react.d.ts` for the full type signatures. The minimal app wiring looks like:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import {
|
|
29
|
+
createClient,
|
|
30
|
+
defineAiAction,
|
|
31
|
+
defineAiActions,
|
|
32
|
+
defineEntities,
|
|
33
|
+
type EntityRecord,
|
|
34
|
+
withAiActions,
|
|
35
|
+
withEntities,
|
|
36
|
+
} from '@howone/sdk'
|
|
37
|
+
import { HowOneProvider, useHowoneContext } from '@howone/sdk/react'
|
|
38
|
+
import { z } from 'zod'
|
|
39
|
+
|
|
40
|
+
const client = createClient({
|
|
41
|
+
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
42
|
+
env: import.meta.env.VITE_HOWONE_ENV,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
export const entities = defineEntities({
|
|
46
|
+
Todo: client.entity<Todo, TodoCreate, TodoUpdate>('Todo'),
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export const ai = defineAiActions({
|
|
50
|
+
summarizeTodo: defineAiAction('summarizeTodo', {
|
|
51
|
+
inputSchema: z.object({ title: z.string() }),
|
|
52
|
+
}),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const howone = withAiActions(withEntities(client, entities), ai)
|
|
56
|
+
export default howone
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Entity calls are plain async SDK calls:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
await howone.entities.Todo.list()
|
|
63
|
+
await howone.entities.Todo.create({ title: 'Ship it', completed: false })
|
|
64
|
+
await howone.entities.Todo.update(id, { completed: true })
|
|
65
|
+
await howone.entities.Todo.delete(id)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
AI action calls are bound by action name:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const result = await howone.ai.summarizeTodo.run({ title })
|
|
72
|
+
const session = howone.ai.summarizeTodo.stream({ title })
|
|
73
|
+
for await (const event of howone.ai.summarizeTodo.events({ title })) {
|
|
74
|
+
// handle event
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
React integration provides provider/auth/loading UI only: `HowOneProvider`, `useHowoneContext`, `FloatingButton`, `Loading`, and `LoadingSpinner`. It does not provide entity/query/AI data hooks. Use `useEffect`/`useState` or a data-fetching library around plain async SDK calls.
|
|
79
|
+
|
|
80
|
+
## Source Order
|
|
81
|
+
|
|
82
|
+
Use this order when deciding what to read:
|
|
83
|
+
|
|
84
|
+
1. `packages/sdk/dist/index.d.ts` and `packages/sdk/dist/react.d.ts` — complete public API.
|
|
85
|
+
2. `references/usage-patterns.md` — generation recipes, examples, traps.
|
|
86
|
+
3. Current app files: `src/lib/sdk.ts`, relevant UI files, and generated manifests.
|
|
18
87
|
|
|
19
88
|
## Rules
|
|
20
89
|
|
|
@@ -24,9 +93,11 @@ Use this skill when writing app code that consumes `@howone/sdk`. The goal is st
|
|
|
24
93
|
- Current action binding shape is `howone.ai.<actionName>.run(input)`, `stream(input)`, and `events(input)`. Do not generate `howone.ai.run.<actionName>(input)` unless the SDK implementation has been changed to support that API.
|
|
25
94
|
- Do not name AI actions `run`, `stream`, or `events`; those are reserved.
|
|
26
95
|
- Define entity create/update types explicitly. Avoid deriving create types from `Omit<EntityRecord & ...>` when it widens the payload.
|
|
96
|
+
- React integration does not provide entity/query/AI hooks. Use plain async SDK calls from React state/effects or an app-level data-fetching library.
|
|
27
97
|
- Never make generated SDK/type files under `.howone`. `.howone` stores manifests only.
|
|
28
98
|
- If an AI-first feature persists generated results, create/update AI capability schema first, sync `.howone/ai`, then derive Entity fields from the AI `outputSchema`.
|
|
29
99
|
|
|
30
100
|
## References
|
|
31
101
|
|
|
32
|
-
Read `
|
|
102
|
+
Read `packages/sdk/dist/index.d.ts` and `packages/sdk/dist/react.d.ts` for the full SDK type surface.
|
|
103
|
+
Read `references/usage-patterns.md` for generation recipes, usage examples, and known traps.
|
|
@@ -1,191 +1,15 @@
|
|
|
1
1
|
# HowOne SDK Usage Patterns
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This file covers what the type declarations don't: generation recipes, usage examples, and known traps. For the complete public API (all types, method signatures, exports), read `node_modules/@howone/sdk/dist/index.d.ts` and `node_modules/@howone/sdk/dist/react.d.ts` first.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## SDK Mental Model
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
defineEntities,
|
|
14
|
-
withAiActions,
|
|
15
|
-
withEntities,
|
|
16
|
-
type CreateClientOptions,
|
|
17
|
-
type EntityRecord,
|
|
18
|
-
type EntityClient,
|
|
19
|
-
type EntityBindings,
|
|
20
|
-
type QueryOptions,
|
|
21
|
-
type QueryResult,
|
|
22
|
-
type PageInfo,
|
|
23
|
-
type DeleteResult,
|
|
24
|
-
type AiClient,
|
|
25
|
-
type AiActionDefinition,
|
|
26
|
-
type AiActionClient,
|
|
27
|
-
type AiActionConfig,
|
|
28
|
-
type AiResult, // = ExecutionResult
|
|
29
|
-
type AiSession, // { result: Promise<AiResult>, cancel(): void, signal: AbortSignal }
|
|
30
|
-
type AiEvent, // SSE event payload
|
|
31
|
-
type AiOptions,
|
|
32
|
-
type UserProfile,
|
|
33
|
-
AiSchemaValidationError,
|
|
34
|
-
} from '@howone/sdk'
|
|
35
|
-
|
|
36
|
-
// @howone/sdk/react — React components and hooks
|
|
37
|
-
import {
|
|
38
|
-
HowOneProvider,
|
|
39
|
-
useHowoneContext,
|
|
40
|
-
FloatingButton,
|
|
41
|
-
Loading,
|
|
42
|
-
LoadingSpinner,
|
|
43
|
-
type HowOneProviderProps,
|
|
44
|
-
type HowOneAuthMode,
|
|
45
|
-
type HowOneThemeMode,
|
|
46
|
-
type HowOneBrandMode,
|
|
47
|
-
} from '@howone/sdk/react'
|
|
48
|
-
|
|
49
|
-
import { z } from 'zod'
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## createClient — signature & return shape
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
const client = createClient({
|
|
58
|
-
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID, // required in Vite apps
|
|
59
|
-
env: import.meta.env.VITE_HOWONE_ENV, // 'local' | 'dev' | 'prod'
|
|
60
|
-
// Optional advanced options (rarely needed):
|
|
61
|
-
// apiUrl, aiUrl, caseStyle ('camel'|'snake'), auth: { mode, getToken }
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
// client shape:
|
|
65
|
-
client.entity<TRecord, TCreate, TUpdate>(entityName: string): EntityClient<...>
|
|
66
|
-
client.entities: Record<string, EntityClient>
|
|
67
|
-
client.ai: AiClient // low-level; prefer withAiActions bindings
|
|
68
|
-
client.me(): Promise<UserProfile | null>
|
|
69
|
-
client.requireMe(): Promise<UserProfile>
|
|
70
|
-
client.auth.setToken(t: string | null): void
|
|
71
|
-
client.auth.getToken(): string | null
|
|
72
|
-
client.auth.isAuthenticated(): boolean
|
|
73
|
-
client.auth.login(redirect?: string): void
|
|
74
|
-
client.auth.logout(): void
|
|
75
|
-
client.upload.file(file, options?): Promise<{ url, thumbnailUrl?, id?, size?, mimeType? }>
|
|
76
|
-
client.upload.image(file): Promise<{ url }>
|
|
77
|
-
client.upload.batch({ files, concurrent?, onProgress?, onFileComplete? }): Promise<BatchUploadResponse>
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## EntityClient — complete method signatures
|
|
83
|
-
|
|
84
|
-
```ts
|
|
85
|
-
type EntityClient<TRecord, TCreate, TUpdate> = {
|
|
86
|
-
name: string
|
|
87
|
-
// Basic CRUD
|
|
88
|
-
list(options?: { page?, limit?, sort?, order?, ...filters }): Promise<TRecord[]>
|
|
89
|
-
get(id: string): Promise<TRecord | null>
|
|
90
|
-
getOrThrow(id: string): Promise<TRecord>
|
|
91
|
-
create(data: TCreate): Promise<TRecord>
|
|
92
|
-
update(id: string, data: TUpdate): Promise<TRecord>
|
|
93
|
-
delete(id: string): Promise<{ deleted: boolean; id: string; message? }>
|
|
94
|
-
// Advanced query with filters/pagination
|
|
95
|
-
query(options?: QueryOptions<TRecord>): Promise<QueryResult<TRecord>>
|
|
96
|
-
query.mine(options?: QueryOptions<TRecord>): Promise<QueryResult<TRecord>>
|
|
97
|
-
bulkCreate(records: TCreate[], options?: { sample?: boolean }): Promise<TRecord[]>
|
|
98
|
-
aggregate(pipeline: unknown[]): Promise<unknown[]>
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// QueryOptions
|
|
102
|
-
type QueryOptions<TRecord> = {
|
|
103
|
-
where?: { [K in keyof TRecord]?: TRecord[K] | FieldOperator }
|
|
104
|
-
// FieldOperator: { eq, ne, gt, gte, lt, lte, contains, like, startsWith, endsWith, in, notIn }
|
|
105
|
-
search?: string
|
|
106
|
-
page?: { number?: number; size?: number }
|
|
107
|
-
orderBy?: Partial<Record<keyof TRecord | string, 'asc' | 'desc'>>
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// QueryResult
|
|
111
|
-
type QueryResult<T> = {
|
|
112
|
-
items: T[]
|
|
113
|
-
page: { number, size, total, totalPages, hasNext, hasPrev }
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// EntityRecord base
|
|
117
|
-
type EntityRecord = {
|
|
118
|
-
id: string
|
|
119
|
-
createdDate?: string
|
|
120
|
-
updatedDate?: string
|
|
121
|
-
createdById?: string
|
|
122
|
-
isSample?: boolean
|
|
123
|
-
[key: string]: unknown
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
## AI Action — complete signatures
|
|
130
|
-
|
|
131
|
-
```ts
|
|
132
|
-
// Define a typed action
|
|
133
|
-
defineAiAction(id: string, config?: {
|
|
134
|
-
inputSchema?: z.ZodType<TInput>
|
|
135
|
-
outputSchema?: z.ZodType<TOutput>
|
|
136
|
-
mode?: 'run' | 'stream' | 'events'
|
|
137
|
-
}): AiActionDefinition<TInput, TOutput>
|
|
138
|
-
|
|
139
|
-
// Group actions
|
|
140
|
-
defineAiActions({ [name]: AiActionDefinition, ... }): actions
|
|
141
|
-
|
|
142
|
-
// Bind to client
|
|
143
|
-
withAiActions(client, actions): client & { ai: { [name]: AiActionClient } }
|
|
144
|
-
|
|
145
|
-
// AiActionClient methods
|
|
146
|
-
howone.ai.<actionName>.run(inputs?, options?): Promise<TOutput>
|
|
147
|
-
howone.ai.<actionName>.stream(inputs?, options?): AiSession
|
|
148
|
-
howone.ai.<actionName>.events(inputs?, options?): AsyncIterable<AiEvent>
|
|
149
|
-
|
|
150
|
-
// AiSession
|
|
151
|
-
type AiSession = {
|
|
152
|
-
result: Promise<AiResult>
|
|
153
|
-
cancel(): void
|
|
154
|
-
signal: AbortSignal
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Reserved names — do NOT use as action names: run, stream, events
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## React: HowOneProvider & hooks
|
|
163
|
-
|
|
164
|
-
`@howone/sdk/react` exports exactly: `HowOneProvider`, `useHowoneContext`, `FloatingButton`, `Loading`, `LoadingSpinner`.
|
|
165
|
-
|
|
166
|
-
**There are no `useEntity`, `useQuery`, `useAi`, or other data-fetching hooks.** Entity and AI calls are plain async functions — call them directly in components with `useEffect`/`useState` or any data-fetching library (SWR, React Query, etc.).
|
|
167
|
-
|
|
168
|
-
```tsx
|
|
169
|
-
// Wrap app root — handles auth, theme, toasts, floating button
|
|
170
|
-
<HowOneProvider
|
|
171
|
-
projectId={...} // optional if client is already configured
|
|
172
|
-
auth="optional" // 'required' | 'optional' | 'none'
|
|
173
|
-
brand="visible" // 'visible' | 'hidden'
|
|
174
|
-
theme="inherit" // 'dark' | 'light' | 'system' | 'inherit'
|
|
175
|
-
showBrandButton={true}
|
|
176
|
-
>
|
|
177
|
-
<App />
|
|
178
|
-
</HowOneProvider>
|
|
179
|
-
|
|
180
|
-
// Auth context hook — the only hook in the package
|
|
181
|
-
const { user, token, isAuthenticated, logout } = useHowoneContext()
|
|
182
|
-
// user shape: { id, email, name, avatar } | null
|
|
183
|
-
|
|
184
|
-
// Loading components
|
|
185
|
-
<Loading size="md" tone="brand" label="..." description="..." fullScreen />
|
|
186
|
-
<LoadingSpinner size="sm" tone="neutral" />
|
|
187
|
-
// size: 'sm' | 'md' | 'lg' tone: 'brand' | 'neutral' | 'inverse'
|
|
188
|
-
```
|
|
7
|
+
- `createClient(...)` creates the base HowOne client with auth, entity, AI, upload, and user helpers.
|
|
8
|
+
- `client.entity<TRecord, TCreate, TUpdate>('EntityName')` creates one typed entity client.
|
|
9
|
+
- `defineEntities(...)` plus `withEntities(client, entities)` makes `howone.entities.<EntityName>` typed in app code.
|
|
10
|
+
- `defineAiAction(...)` plus `defineAiActions(...)` plus `withAiActions(...)` makes `howone.ai.<actionName>.run|stream|events` typed in app code.
|
|
11
|
+
- `@howone/sdk/react` provides auth/theme/loading UI primitives only. It does not provide entity, query, or AI data hooks.
|
|
12
|
+
- `.howone/database/manifest.json` and `.howone/ai/manifest.json` are the generated contracts. App source should be generated from those manifests, not from memory.
|
|
189
13
|
|
|
190
14
|
## App SDK Entry
|
|
191
15
|
|
|
@@ -215,7 +39,6 @@ export const entities = defineEntities({
|
|
|
215
39
|
export const ai = defineAiActions({
|
|
216
40
|
// generateImage: defineAiAction('generateImage', {
|
|
217
41
|
// inputSchema: generateImageInputSchema,
|
|
218
|
-
// outputSchema: generateImageOutputSchema,
|
|
219
42
|
// }),
|
|
220
43
|
})
|
|
221
44
|
|
|
@@ -224,6 +47,29 @@ const howone = withAiActions(withEntities(client, entities), ai)
|
|
|
224
47
|
export default howone
|
|
225
48
|
```
|
|
226
49
|
|
|
50
|
+
## Manifest-To-Code Recipe
|
|
51
|
+
|
|
52
|
+
When backend metadata has been synced, generate app SDK code in this order:
|
|
53
|
+
|
|
54
|
+
1. Read `src/lib/sdk.ts` to preserve the template's import style and existing bindings.
|
|
55
|
+
2. Read `.howone/database/manifest.json` for entity names and fields. Generate `Record`, `Create`, and `Update` types, then bind each entity with `client.entity<Record, Create, Update>('EntityName')`.
|
|
56
|
+
3. Read `.howone/ai/manifest.json` for AI capability names and JSON schemas. Generate zod input schemas and, only when safe, output schemas.
|
|
57
|
+
4. Export a single default `howone` client built by composing `withEntities` and `withAiActions`.
|
|
58
|
+
5. In UI code, import the default client and call `howone.entities.<Entity>.*` or `howone.ai.<action>.run(...)`.
|
|
59
|
+
|
|
60
|
+
Basic JSON schema mapping for generated zod:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
string -> z.string()
|
|
64
|
+
number -> z.number()
|
|
65
|
+
integer -> z.number().int()
|
|
66
|
+
boolean -> z.boolean()
|
|
67
|
+
array -> z.array(...)
|
|
68
|
+
object -> z.object({ ... })
|
|
69
|
+
enum -> z.enum([...])
|
|
70
|
+
missing from required[] -> .optional()
|
|
71
|
+
```
|
|
72
|
+
|
|
227
73
|
## Entity Types
|
|
228
74
|
|
|
229
75
|
Prefer explicit payload types:
|
|
@@ -248,6 +94,22 @@ export const entities = defineEntities({
|
|
|
248
94
|
})
|
|
249
95
|
```
|
|
250
96
|
|
|
97
|
+
Generated entity app code usually needs only these calls:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const result = await howone.entities.Story.query({
|
|
101
|
+
search,
|
|
102
|
+
page: { number: 1, size: 20 },
|
|
103
|
+
orderBy: { createdDate: 'desc' },
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
await howone.entities.Story.create({ title, content, prompt })
|
|
107
|
+
await howone.entities.Story.update(id, { title })
|
|
108
|
+
await howone.entities.Story.delete(id)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Prefer `query()` for list UIs that need paging/search/sort metadata. Use `list()` only for simple array reads.
|
|
112
|
+
|
|
251
113
|
Avoid this for generated create payloads:
|
|
252
114
|
|
|
253
115
|
```ts
|
|
@@ -277,7 +139,6 @@ export type GenerateStoryOutput = z.infer<typeof generateStoryOutputSchema>
|
|
|
277
139
|
export const ai = defineAiActions({
|
|
278
140
|
generateStory: defineAiAction('generateStory', {
|
|
279
141
|
inputSchema: generateStoryInputSchema,
|
|
280
|
-
outputSchema: generateStoryOutputSchema,
|
|
281
142
|
mode: 'run',
|
|
282
143
|
}),
|
|
283
144
|
})
|
|
@@ -290,6 +151,7 @@ const result = await howone.ai.generateStory.run({
|
|
|
290
151
|
topic,
|
|
291
152
|
ageRange,
|
|
292
153
|
})
|
|
154
|
+
const story = result.finalResult as GenerateStoryOutput
|
|
293
155
|
```
|
|
294
156
|
|
|
295
157
|
For streaming:
|
|
@@ -311,15 +173,15 @@ for await (const event of howone.ai.generateStory.events(input)) {
|
|
|
311
173
|
|
|
312
174
|
`run()` validates input with `inputSchema`, calls the workflow, then validates the **raw `ExecutionResult`** (the full SSE envelope) with `outputSchema`. It does **not** automatically unwrap `result.finalResult`.
|
|
313
175
|
|
|
314
|
-
If you want to type the final result only,
|
|
315
|
-
1. Define `outputSchema` to match the full `ExecutionResult` shape, or
|
|
316
|
-
2. Omit `outputSchema` and unwrap manually:
|
|
176
|
+
If you want to type the final result only, omit `outputSchema` and unwrap manually:
|
|
317
177
|
|
|
318
178
|
```ts
|
|
319
179
|
const result = await howone.ai.generateStory.run(input) // returns AiResult
|
|
320
180
|
const finalResult = result.finalResult as GenerateStoryOutput
|
|
321
181
|
```
|
|
322
182
|
|
|
183
|
+
Only define `outputSchema` when it matches the full `ExecutionResult` envelope or after verifying the SDK has changed to validate `finalResult` directly.
|
|
184
|
+
|
|
323
185
|
## AI Result Persistence
|
|
324
186
|
|
|
325
187
|
For AI-generated data that should be saved:
|
|
@@ -333,10 +195,11 @@ Example:
|
|
|
333
195
|
|
|
334
196
|
```ts
|
|
335
197
|
const story = await howone.ai.generateStory.run(input)
|
|
198
|
+
const finalResult = story.finalResult as GenerateStoryOutput
|
|
336
199
|
|
|
337
200
|
await howone.entities.Story.create({
|
|
338
|
-
title:
|
|
339
|
-
content:
|
|
201
|
+
title: finalResult.title,
|
|
202
|
+
content: finalResult.content,
|
|
340
203
|
prompt: input.topic,
|
|
341
204
|
})
|
|
342
205
|
```
|