Skip to content

Commit d027965

Browse files
committed
style: prettier format
1 parent 84f63ab commit d027965

23 files changed

+485
-331
lines changed

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22

33
This is a web UI for https://github.com/dzhng/deep-research. It supports streaming AI responses, and viasualization of the research process using a tree structure.
44

5-
Note: The project is currently WIP, expect bugs. README will be updated once the project is usable.
6-
7-
Rough preview of the UI:
8-
9-
<img width="1087" alt="image" src="https://github.com/user-attachments/assets/4bb5b722-0300-4d4f-bb01-fc1ed2404442" />
5+
> Note: The project is currently WIP, expect bugs.
106
7+
<video src="https://github.com/user-attachments/assets/c3738551-b258-47c6-90a8-fd097e5165c8"></video>
118

129
## Setup
1310

@@ -80,3 +77,7 @@ bun run preview
8077
```
8178

8279
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
80+
81+
## License
82+
83+
MIT

app.vue

+17-4
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@
1818
content:
1919
'SiliconCloud Stats 是一个用于分析 SiliconCloud 平台使用情况的工具。通过输入 Cookie,可以拉取 SiliconCloud 控制台 API 来实现各种分析功能,如 token 用量分析等。',
2020
},
21-
{ name: 'keywords', content: 'SiliconCloud, 数据分析, token 用量, API 分析, 控制台工具' },
21+
{
22+
name: 'keywords',
23+
content: 'SiliconCloud, 数据分析, token 用量, API 分析, 控制台工具',
24+
},
2225
// Open Graph tags
23-
{ property: 'og:title', content: 'SiliconCloud Stats - SiliconCloud 平台使用情况分析工具' },
26+
{
27+
property: 'og:title',
28+
content: 'SiliconCloud Stats - SiliconCloud 平台使用情况分析工具',
29+
},
2430
{
2531
property: 'og:description',
2632
content:
@@ -30,8 +36,15 @@
3036
{ property: 'og:image', content: '/images/readme-showcase-total.webp' },
3137
// Twitter Card tags
3238
{ name: 'twitter:card', content: 'summary_large_image' },
33-
{ name: 'twitter:title', content: 'SiliconCloud Stats - SiliconCloud 平台使用情况分析工具' },
34-
{ name: 'twitter:description', content: 'SiliconCloud Stats 是一个用于分析 SiliconCloud 平台使用情况的工具。' },
39+
{
40+
name: 'twitter:title',
41+
content: 'SiliconCloud Stats - SiliconCloud 平台使用情况分析工具',
42+
},
43+
{
44+
name: 'twitter:description',
45+
content:
46+
'SiliconCloud Stats 是一个用于分析 SiliconCloud 平台使用情况的工具。',
47+
},
3548
{ name: 'twitter:image', content: '/images/readme-showcase-total.webp' },
3649
],
3750
// script: [

components/ColorModeButton.vue

+5-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919

2020
<template>
2121
<div>
22-
<UButton :icon="preference === 'dark' ? 'i-lucide-sun' : 'i-lucide-moon'" color="primary" @click="toggleColorMode" />
22+
<UButton
23+
:icon="preference === 'dark' ? 'i-lucide-sun' : 'i-lucide-moon'"
24+
color="primary"
25+
@click="toggleColorMode"
26+
/>
2327
</div>
2428
</template>

components/DeepResearch.vue

+20-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<script setup lang="ts">
2-
import { deepResearch, type PartialSearchResult, type ResearchResult, type ResearchStep } from '~/lib/deep-research'
2+
import {
3+
deepResearch,
4+
type PartialSearchResult,
5+
type ResearchResult,
6+
type ResearchStep,
7+
} from '~/lib/deep-research'
38
import type { TreeNode } from './Tree.vue'
49
510
const emit = defineEmits<{
@@ -44,7 +49,10 @@
4449
tree.value.children.push(node)
4550
} else {
4651
// 找到父节点并添加
47-
const parentNode = findNode(tree.value, getParentNodeId(step.nodeId))
52+
const parentNode = findNode(
53+
tree.value,
54+
getParentNodeId(step.nodeId),
55+
)
4856
if (parentNode) {
4957
parentNode.children.push(node)
5058
}
@@ -160,7 +168,8 @@
160168
<template #header>
161169
<h2 class="font-bold">3. Web Browsing</h2>
162170
<p class="text-sm text-gray-500">
163-
The AI will then search the web based on our research goal, and iterate until the depth is reached.
171+
The AI will then search the web based on our research goal, and iterate
172+
until the depth is reached.
164173
<br />
165174
Click a child node to view details.
166175
</p>
@@ -174,7 +183,9 @@
174183
<h2 class="text-xl font-bold mt-2">{{ selectedNode.label }}</h2>
175184

176185
<!-- Root node has no additional information -->
177-
<p v-if="selectedNode.id === '0'"> This is the beginning of your deep research journey! </p>
186+
<p v-if="selectedNode.id === '0'">
187+
This is the beginning of your deep research journey!
188+
</p>
178189
<template v-else>
179190
<h3 class="text-lg font-semibold mt-2">Research Goal:</h3>
180191
<p>{{ selectedNode.researchGoal }}</p>
@@ -188,7 +199,11 @@
188199

189200
<h3 class="text-lg font-semibold mt-2">Learnings:</h3>
190201
<ul class="list-disc list-inside">
191-
<li v-for="(learning, index) in selectedNode.learnings" :key="index">{{ learning }}</li>
202+
<li
203+
v-for="(learning, index) in selectedNode.learnings"
204+
:key="index"
205+
>{{ learning }}</li
206+
>
192207
</ul>
193208
</template>
194209
</div>

components/ResearchFeedback.vue

+9-2
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,22 @@
7474
<UCard>
7575
<template #header>
7676
<h2 class="font-bold">2. Model Feedback</h2>
77-
<p class="text-sm text-gray-500"> The AI will ask you some follow up questions to help you clarify the research direction. </p>
77+
<p class="text-sm text-gray-500">
78+
The AI will ask you some follow up questions to help you clarify the
79+
research direction.
80+
</p>
7881
</template>
7982

8083
<div class="flex flex-col gap-2">
8184
<p v-if="error" class="text-red-500">{{ error }}</p>
8285
<div v-if="!feedback.length && !error">Waiting for model feedback...</div>
8386
<template v-else>
8487
<div v-if="error" class="text-red-500">{{ error }}</div>
85-
<div v-for="(feedback, index) in feedback" class="flex flex-col gap-2" :key="index">
88+
<div
89+
v-for="(feedback, index) in feedback"
90+
class="flex flex-col gap-2"
91+
:key="index"
92+
>
8693
Assistant: {{ feedback.assistantQuestion }}
8794
<UInput v-model="feedback.userAnswer" />
8895
</div>

components/ResearchForm.vue

+42-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
numQuestions: 3,
2222
})
2323
24-
const isSubmitButtonDisabled = computed(() => !form.query || !form.breadth || !form.depth || !form.numQuestions)
24+
const isSubmitButtonDisabled = computed(
25+
() => !form.query || !form.breadth || !form.depth || !form.numQuestions,
26+
)
2527
2628
function handleSubmit() {
2729
emit('submit', {
@@ -41,29 +43,63 @@
4143
</template>
4244
<div class="flex flex-col gap-2">
4345
<UFormField label="Research Topic" required>
44-
<UTextarea class="w-full" v-model="form.query" :rows="3" placeholder="Enter whatever you want to research..." required />
46+
<UTextarea
47+
class="w-full"
48+
v-model="form.query"
49+
:rows="3"
50+
placeholder="Enter whatever you want to research..."
51+
required
52+
/>
4553
</UFormField>
4654

4755
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
4856
<UFormField label="Number of Questions" required>
4957
<template #help> Number of questions for you to clarify. </template>
50-
<UInput v-model="form.numQuestions" class="w-full" type="number" :min="1" :max="5" :step="1" />
58+
<UInput
59+
v-model="form.numQuestions"
60+
class="w-full"
61+
type="number"
62+
:min="1"
63+
:max="5"
64+
:step="1"
65+
/>
5166
</UFormField>
5267

5368
<UFormField label="Depth" required>
5469
<template #help> How deep you want to dig. </template>
55-
<UInput v-model="form.depth" class="w-full" type="number" :min="1" :max="5" :step="1" />
70+
<UInput
71+
v-model="form.depth"
72+
class="w-full"
73+
type="number"
74+
:min="1"
75+
:max="5"
76+
:step="1"
77+
/>
5678
</UFormField>
5779

5880
<UFormField label="Breadth" required>
5981
<template #help> Number of searches in each depth. </template>
60-
<UInput v-model="form.breadth" class="w-full" type="number" :min="1" :max="5" :step="1" />
82+
<UInput
83+
v-model="form.breadth"
84+
class="w-full"
85+
type="number"
86+
:min="1"
87+
:max="5"
88+
:step="1"
89+
/>
6190
</UFormField>
6291
</div>
6392
</div>
6493

6594
<template #footer>
66-
<UButton type="submit" color="primary" :loading="isLoadingFeedback" :disabled="isSubmitButtonDisabled" block @click="handleSubmit">
95+
<UButton
96+
type="submit"
97+
color="primary"
98+
:loading="isLoadingFeedback"
99+
:disabled="isSubmitButtonDisabled"
100+
block
101+
@click="handleSubmit"
102+
>
67103
{{ isLoadingFeedback ? 'Researching...' : 'Start Research' }}
68104
</UButton>
69105
</template>

components/ResearchReport.vue

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<script setup lang="ts">
22
import { marked } from 'marked'
3-
import { writeFinalReport, type WriteFinalReportParams } from '~/lib/deep-research'
3+
import {
4+
writeFinalReport,
5+
type WriteFinalReportParams,
6+
} from '~/lib/deep-research'
47
58
interface CustomReportParams extends WriteFinalReportParams {
69
visitedUrls: string[]
@@ -10,8 +13,12 @@
1013
const loading = ref(false)
1114
const loadingExportPdf = ref(false)
1215
const reportContent = ref('')
13-
const reportHtml = computed(() => marked(reportContent.value, { gfm: true, silent: true }))
14-
const isExportButtonDisabled = computed(() => !reportContent.value || loading.value || loadingExportPdf.value)
16+
const reportHtml = computed(() =>
17+
marked(reportContent.value, { gfm: true, silent: true }),
18+
)
19+
const isExportButtonDisabled = computed(
20+
() => !reportContent.value || loading.value || loadingExportPdf.value,
21+
)
1522
1623
async function generateReport(params: CustomReportParams) {
1724
loading.value = true
@@ -113,7 +120,9 @@
113120
/>
114121
<template v-else>
115122
<div v-if="error" class="text-red-500">{{ error }}</div>
116-
<div v-else>{{ loading ? 'Generating report...' : 'Waiting for report..' }}.</div>
123+
<div v-else
124+
>{{ loading ? 'Generating report...' : 'Waiting for report..' }}.</div
125+
>
117126
</template>
118127
</UCard>
119128
</template>

components/Tree.vue

+6-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,12 @@
7272
</UButton>
7373
<ol v-if="node.children.length > 0" class="space-y-2">
7474
<li v-for="node in node.children" :key="node.id">
75-
<Tree class="ml-2" :node="node" :selected-node @select="emit('select', $event)" />
75+
<Tree
76+
class="ml-2"
77+
:node="node"
78+
:selected-node
79+
@select="emit('select', $event)"
80+
/>
7681
</li>
7782
</ol>
7883
</div>

lib/ai/providers.ts

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,58 @@
1-
import { createOpenAI } from '@ai-sdk/openai';
2-
import { getEncoding } from 'js-tiktoken';
1+
import { createOpenAI } from '@ai-sdk/openai'
2+
import { getEncoding } from 'js-tiktoken'
33

4-
import { RecursiveCharacterTextSplitter } from './text-splitter';
4+
import { RecursiveCharacterTextSplitter } from './text-splitter'
55

66
// Providers
77
const openai = createOpenAI({
88
apiKey: import.meta.env.VITE_OPENAI_API_KEY!,
99
baseURL: import.meta.env.VITE_OPENAI_ENDPOINT || 'https://api.openai.com/v1',
10-
});
10+
})
1111

12-
const customModel = import.meta.env.VITE_OPENAI_MODEL || 'o3-mini';
12+
const customModel = import.meta.env.VITE_OPENAI_MODEL || 'o3-mini'
1313

1414
// Models
1515

1616
export const o3MiniModel = openai(customModel, {
1717
// reasoningEffort: customModel.startsWith('o') ? 'medium' : undefined,
1818
structuredOutputs: true,
19-
});
19+
})
2020

21-
const MinChunkSize = 140;
22-
const encoder = getEncoding('o200k_base');
21+
const MinChunkSize = 140
22+
const encoder = getEncoding('o200k_base')
2323

2424
// trim prompt to maximum context size
2525
export function trimPrompt(
2626
prompt: string,
2727
contextSize = Number(import.meta.env.VITE_CONTEXT_SIZE) || 128_000,
2828
) {
2929
if (!prompt) {
30-
return '';
30+
return ''
3131
}
3232

33-
const length = encoder.encode(prompt).length;
33+
const length = encoder.encode(prompt).length
3434
if (length <= contextSize) {
35-
return prompt;
35+
return prompt
3636
}
3737

38-
const overflowTokens = length - contextSize;
38+
const overflowTokens = length - contextSize
3939
// on average it's 3 characters per token, so multiply by 3 to get a rough estimate of the number of characters
40-
const chunkSize = prompt.length - overflowTokens * 3;
40+
const chunkSize = prompt.length - overflowTokens * 3
4141
if (chunkSize < MinChunkSize) {
42-
return prompt.slice(0, MinChunkSize);
42+
return prompt.slice(0, MinChunkSize)
4343
}
4444

4545
const splitter = new RecursiveCharacterTextSplitter({
4646
chunkSize,
4747
chunkOverlap: 0,
48-
});
49-
const trimmedPrompt = splitter.splitText(prompt)[0] ?? '';
48+
})
49+
const trimmedPrompt = splitter.splitText(prompt)[0] ?? ''
5050

5151
// last catch, there's a chance that the trimmed prompt is same length as the original prompt, due to how tokens are split & innerworkings of the splitter, handle this case by just doing a hard cut
5252
if (trimmedPrompt.length === prompt.length) {
53-
return trimPrompt(prompt.slice(0, chunkSize), contextSize);
53+
return trimPrompt(prompt.slice(0, chunkSize), contextSize)
5454
}
5555

5656
// recursively trim until the prompt is within the context size
57-
return trimPrompt(trimmedPrompt, contextSize);
57+
return trimPrompt(trimmedPrompt, contextSize)
5858
}

0 commit comments

Comments
 (0)