Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(deps-dev): bump @astrojs/solid-js from 4.4.4 to 5.0.3 #265

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions README.md

This file was deleted.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@tiptap/pm": "^2.9.1",
"@tiptap/starter-kit": "^2.9.1",
"array-move": "^4.0.0",
"astro": "4.16.11",
"astro": "4.16.13",
"classnames": "^2.5.1",
"dedent": "^1.5.3",
"dexie": "^4.0.9",
Expand All @@ -43,7 +43,7 @@
"vite-imagetools": "^7.0.4"
},
"devDependencies": {
"@astrojs/solid-js": "^4.4.2",
"@astrojs/solid-js": "^5.0.3",
"@biomejs/biome": "^1.9.4",
"@types/dompurify": "^3.0.5",
"@types/fs-extra": "^11.0.4",
Expand Down
3 changes: 3 additions & 0 deletions src/integrations/cloudflare/server/server.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { App } from 'astro/app'
// @ts-check
import { Hono } from 'hono'
import { basicAuth } from 'hono/basic-auth'

export function createExports(manifest: import('astro').SSRManifest) {
const app = new App(manifest)

const honoApp = new Hono()

honoApp.use(basicAuth({ verifyUser: (username, password, c) => true }))

honoApp.all('/*', async (c) => {
const res = await app.render(c.req.raw)
return res
Expand Down
12 changes: 6 additions & 6 deletions src/islands/ai-quiz/QuizScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import {
For,
Show,
createEffect,
createMemo,
createSignal,
For,
onMount,
Show,
} from 'solid-js'
import { QuizManager, type GeneratedQuiz } from './quiz-manager'
import type { MargedNoteData } from '../note/components/notes-utils'
import { QuizDB, type Quizzes } from './storage'
import type { QuizContent } from './constants'
import { shuffle } from '../../utils/arr'
import { icon } from '../../utils/icons'
import type { MargedNoteData } from '../note/components/notes-utils'
import type { TextNoteData } from '../note/components/notes/TextNote/types'
import type { QuizContent } from './constants'
import { type GeneratedQuiz, QuizManager } from './quiz-manager'
import { QuizDB, type Quizzes } from './storage'
import './QuizScreen.css'

export const finish = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/islands/ai-quiz/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const PROMPT_TO_GENERATE_SELECT_QUIZ = `

ユーザーからの文章に書かれていない問題は絶対に生成しないでください。

以下のJSONスキーマに従いなさい
以下のJSONスキーマに従ったJSONを出力しなさい。また、出力はトップレベルで配列です
${JSON.stringify(selectQuestion)}
`.trim()

Expand Down
10 changes: 5 additions & 5 deletions src/islands/ai-quiz/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { createSignal, onMount, Show } from 'solid-js'
import type { NoteLoadType } from '../note/note-load-types'
import { loadNoteFromType } from '../shared/storage'
import { load } from '../note/utils/file-format'
import { Show, createSignal, onMount } from 'solid-js'
import type { MargedNoteData } from '../note/components/notes-utils'
import { Spinner } from '../note/components/utils/Spinner'
import { finish, QuizScreen } from './QuizScreen'
import type { NoteLoadType } from '../note/note-load-types'
import { load } from '../note/utils/file-format'
import { loadNoteFromType } from '../shared/storage'
import { QuizScreen, finish } from './QuizScreen'

export const Navbar = () => {}

Expand Down
31 changes: 12 additions & 19 deletions src/islands/ai-quiz/quiz-manager.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,31 @@
import { parse, safeParse } from 'valibot'
import { shuffle } from '../../utils/arr'
import type { MargedNoteData } from '../note/components/notes-utils'
import type { TextNoteData } from '../note/components/notes/TextNote/types'
import { getGoogleGenerativeAI } from '../shared/gemini'
import { sha256 } from '../shared/hash'
import { generate } from '../shared/llm'
import {
CONTENT_SCHEMA,
PROMPT_TO_GENERATE_SELECT_QUIZ,
type QuizContent,
} from './constants'
import type { MargedNoteData } from '../note/components/notes-utils'
import type { TextNoteData } from '../note/components/notes/TextNote/types'
import type { QuizDB, Quizzes } from './storage'
import { shuffle } from '../../utils/arr'
import { sha256 } from '../shared/hash'

const generateQuizzesFromAI = async (text: string): Promise<QuizContent[]> => {
const gemini = getGoogleGenerativeAI()
if (!gemini) {
throw new Error('Gemini is null.')
}
const response = await gemini
.getGenerativeModel({
model: 'gemini-1.5-flash',
generationConfig: {
responseMimeType: 'application/json',
},
systemInstruction: {
role: 'system',
parts: [{ text: PROMPT_TO_GENERATE_SELECT_QUIZ }],
},
})
.startChat()
.sendMessage(text)

const response = await generate({
systemPrompt: PROMPT_TO_GENERATE_SELECT_QUIZ,
userPrompt: text,
jsonMode: true,
})

let json: unknown
try {
json = JSON.parse(response.response.text())
json = JSON.parse(response)
} catch {
return []
}
Expand Down
146 changes: 32 additions & 114 deletions src/islands/note/components/notes/TextNote/AI.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
import {
type GenerateContentResponse,
type GenerateContentStreamResult,
type GoogleGenerativeAI,
GoogleGenerativeAIFetchError,
GoogleGenerativeAIResponseError,
} from '@google/generative-ai'
import markdownIt from 'markdown-it'
import {
Match,
Expand All @@ -19,14 +12,14 @@ import {
onMount,
untrack,
} from 'solid-js'
import { getGoogleGenerativeAI } from '../../../../shared/gemini'
import { generate as generateLLM, generateStream } from '../../../../shared/llm'
import { Spinner } from '../../utils/Spinner'

const markdownParser = markdownIt()

type Mode = 'text' | 'image'
type Generate = {
generate: () => Promise<GenerateContentStreamResult>
generate: () => ReturnType<typeof generateStream>
}

const renderMarkdown = (markdown: string) =>
Expand Down Expand Up @@ -82,16 +75,6 @@ const FROM_TEXT_PLACEHOLDER_CONTENTS: string[] = [
'英語の曜日についての暗記シート',
]

const getGemini = (): GoogleGenerativeAI => {
const ai = getGoogleGenerativeAI()
if (!ai) {
if (confirm('AI 機能が設定されていません。\n設定を開きますか?')) {
location.href = '/app/settings#ai'
}
throw 0
}
return ai
}
export const FromText = (props: {
setStream(stream: Generate): void
initPrompt?: string
Expand Down Expand Up @@ -161,24 +144,13 @@ export const FromText = (props: {

const generate = async () => {
setIsPreprocessing(true)
const gemini = getGemini()
const model = gemini.getGenerativeModel({
model: 'gemini-1.5-flash',
})
props.setStream({
generate: () =>
model
.startChat({
systemInstruction: {
role: 'model',
parts: [
{
text: 'ユーザーの指示に基づき、暗記の手助けになる赤シート用文章を生成しなさい。赤シートで隠すべき単語は、Markdownの太字機能で表現しなさい。隠す必要がない場所には太字は使わないでください。太字の場所はユーザーに見えません。また、単語の一覧を箇条書きにすることはしないでください。',
},
],
},
})
.sendMessageStream(getPrompt()),
generateStream({
systemPrompt:
'ユーザーの指示に基づき、暗記の手助けになる赤シート用文章を生成しなさい。赤シートで隠すべき単語は、Markdownの太字機能で表現しなさい。隠す必要がない場所には太字は使わないでください。太字の場所はユーザーに見えません。また、単語の一覧を箇条書きにすることはしないでください。',
userPrompt: getPrompt(),
}),
})
setIsPreprocessing(false)
}
Expand Down Expand Up @@ -235,40 +207,22 @@ export const FromImage = (props: {
return
}

const ai = getGemini()
const model = ai.getGenerativeModel({
model: 'gemini-1.5-pro',
})
const b64 = await new Promise<string>((resolve) => {
const reader = new FileReader()
reader.onloadend = () => resolve((reader.result as string).split(',')[1]!)
reader.readAsDataURL(file)
})

const generate = () =>
model
.startChat({
systemInstruction: {
role: 'model',
parts: [
{
text: '画像を抽出し、そっくりそのまま書き出しなさい。省略せずに画像の文字全てを書き出すこと。画像に書いていないことは書かないこと。また、ユーザーからの指示があれば、条件を満たす場所をMarkdownの太字で表現しなさい。',
},
],
},
})
.sendMessageStream([
{
text: '',
},
{
inlineData: {
mimeType: file.type,
data: b64,
},
},
])
props.setStream({ generate })
const generate = async () =>
await generateStream({
systemPrompt:
'画像を抽出し、そっくりそのまま書き出しなさい。省略せずに画像の文字全てを書き出すこと。画像に書いていないことは書かないこと。また、ユーザーからの指示があれば、条件を満たす場所をMarkdownの太字で表現しなさい。',
image: { mimeType: file.type, data: b64 },
userPrompt: '',
})
props.setStream({
generate,
})
setIsPreprocessing(false)
}

Expand All @@ -284,9 +238,12 @@ export const FromImage = (props: {
>
<div class="h-[70dvh] max-h-[70dvh] grid grid-rows-2 grid-cols-1 md:grid-rows-1 md:grid-cols-2">
<div>
<div style={{
'background-image': `url("${getScanedImageURL()}")`
}} class="w-full h-full bg-no-repeat bg-center bg-contain" />
<div
style={{
'background-image': `url("${getScanedImageURL()}")`,
}}
class="w-full h-full bg-no-repeat bg-center bg-contain"
/>
</div>
<div class="h-full">
<label class="h-full flex flex-col">
Expand Down Expand Up @@ -369,60 +326,21 @@ export const AIDialogCore = (props: {
if (!streamer) {
return
}
let stream: GenerateContentStreamResult
let stream: Awaited<ReturnType<typeof generateStream>>
try {
stream = await streamer.generate()
} catch (error) {
if (error instanceof GoogleGenerativeAIFetchError) {
switch (error.status) {
case 429:
setGenerateError({ type: 'RATE_LIMIT', isFetchError: true })
break
case 500:
case 503:
setGenerateError({
type: 'SERVICE_UNAVAILABLE',
isFetchError: true,
})
break
default:
setGenerateError({ type: 'UNKNOWN', isFetchError: true })
break
}
setIsGenerating(false)
return
}
} catch (e) {
console.error(e)
setIsGenerating(false)
setGenerateError({ type: 'UNKNOWN', isFetchError: true })
return
}
try {
for await (const chunk of stream.stream) {
setGenerated(`${getGenerated()}${chunk.text()}`)
outputRef.scrollTo({
behavior: 'smooth',
top: 100 + outputRef.scrollHeight,
})
}
} catch (error) {
if (error instanceof GoogleGenerativeAIResponseError) {
const response = (
error as GoogleGenerativeAIResponseError<GenerateContentResponse>
).response
if (response?.candidates?.[0]?.finishReason === 'SAFETY') {
setGenerateError({
type: 'SAFETY',
isFetchError: false,
})
return
}
}
setGenerateError({
type: 'UNKNOWN',
isFetchError: false,
})
} finally {
setIsGenerating(false)
let text = ''
for await (const chunk of stream) {
text += chunk
setGenerated(text)
}
setIsGenerating(false)
}
createEffect(() => {
getStream()
Expand Down
Loading
Loading