Skip to content

Commit 3d26d1d

Browse files
committed
feat: get body text, stream answers, modify config
1 parent 13ae270 commit 3d26d1d

File tree

5 files changed

+187
-56
lines changed

5 files changed

+187
-56
lines changed

manifest.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,16 @@
44
"description": "页面 AI",
55
"version": "1.0",
66
"action": {
7+
"default_title": "Page AI",
78
"default_popup": "popup/popup.html",
89
"default_icon": "assets/logo.png"
910
},
10-
"host_permissions": ["https://*/"]
11+
"host_permissions": ["<all_urls>"],
12+
"permissions": ["activeTab", "storage"],
13+
"content_scripts": [
14+
{
15+
"matches": ["<all_urls>"],
16+
"js": ["src/content.js"]
17+
}
18+
]
1119
}

popup/popup.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ body {
2121
background-color: #eceff7;
2222
}
2323

24+
input {
25+
border: 1px solid rgb(210, 210, 210);
26+
}
27+
28+
input:focus-visible {
29+
outline: none;
30+
}
31+
2432
#app {
2533
padding: 5px 10px 10px;
2634
}
@@ -29,6 +37,27 @@ body {
2937
text-align: center;
3038
}
3139

40+
.config {
41+
margin-top: 10px;
42+
padding-bottom: 10px;
43+
border-bottom: 1px solid #cecece;
44+
}
45+
46+
.config .item {
47+
display: flex;
48+
justify-content: space-between;
49+
padding: 3px 10px;
50+
}
51+
52+
.config .item span {
53+
width: 60px;
54+
}
55+
56+
.config .item .config-ipt {
57+
flex: 1;
58+
border-radius: 5px;
59+
}
60+
3261
.box .search {
3362
display: flex;
3463
justify-content: center;

popup/popup.html

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,38 @@
1-
<html>
2-
<head>
3-
<meta charset="UTF-8" />
4-
<title>页面 AI</title>
5-
<link rel="stylesheet" href="./popup.css" />
6-
</head>
1+
<meta charset="UTF-8" />
2+
<link rel="stylesheet" href="./popup.css" />
73

8-
<body>
9-
<div id="app">
10-
<h1 class="title">页面 AI</h1>
4+
<div id="app">
5+
<h1 class="title">页面 AI</h1>
116

12-
<div class="box">
13-
<div class="search">
14-
<input type="text" class="search-ipt" placeholder="" />
15-
<button class="search-btn">
16-
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
17-
<path
18-
d="M7 11L12 6L17 11M12 18V7"
19-
stroke="currentColor"
20-
stroke-width="2"
21-
stroke-linecap="round"
22-
stroke-linejoin="round"
23-
></path>
24-
</svg>
25-
</button>
26-
</div>
7+
<div class="config">
8+
<div class="item">
9+
<span>Base URL</span>
10+
<input type="text" class="config-ipt" data-name="BASE_URL" />
11+
</div>
12+
<div class="item">
13+
<span>API Key</span>
14+
<input type="password" class="config-ipt" data-name="API_KEY" />
15+
</div>
16+
</div>
2717

28-
<div class="message-list"></div>
29-
</div>
18+
<div class="box">
19+
<div class="search">
20+
<input type="text" class="search-ipt" placeholder="" />
21+
<button class="search-btn">
22+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
23+
<path
24+
d="M7 11L12 6L17 11M12 18V7"
25+
stroke="currentColor"
26+
stroke-width="2"
27+
stroke-linecap="round"
28+
stroke-linejoin="round"
29+
></path>
30+
</svg>
31+
</button>
3032
</div>
3133

32-
<script src="./popup.js"></script>
33-
</body>
34-
</html>
34+
<div class="message-list"></div>
35+
</div>
36+
</div>
37+
38+
<script src="./popup.js"></script>

popup/popup.js

Lines changed: 108 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,134 @@
1-
const API_KEY = ''
1+
const configIpts = document.querySelectorAll('.config-ipt')
22

33
const searchInput = document.querySelector('.search-ipt')
44
const searchBtn = document.querySelector('.search-btn')
5-
65
const messageList = document.querySelector('.message-list')
76

8-
searchBtn.addEventListener('click', async () => {
9-
const searchContent = searchInput.value
10-
console.log('input', searchContent)
7+
let currentTab = null
8+
const config = {
9+
BASE_URL: '',
10+
API_KEY: ''
11+
}
12+
let searchContent = ''
13+
14+
init()
15+
function init() {
16+
chrome.tabs
17+
.query({
18+
active: true,
19+
lastFocusedWindow: true
20+
})
21+
.then(([tab]) => (currentTab = tab))
22+
23+
chrome.storage.local.get(['BASE_URL', 'API_KEY']).then((res) => {
24+
config.BASE_URL = res.BASE_URL ?? ''
25+
config.API_KEY = res.API_KEY ?? ''
26+
27+
configIpts.forEach((item) => {
28+
const name = item.dataset.name
29+
if (name === 'BASE_URL') {
30+
item.value = config.BASE_URL
31+
} else {
32+
item.value = config.API_KEY
33+
}
1134

12-
const div = document.createElement('div')
13-
div.setAttribute('class', 'item')
35+
item.addEventListener('change', (evnet) => {
36+
const name = evnet.target.dataset.name
37+
const value = evnet.target.value
38+
39+
chrome.storage.local.set({ [name]: value })
40+
})
41+
})
42+
})
1443

15-
const aiResponseMessage = await fetchOpenAI(searchContent)
44+
searchBtn.addEventListener('click', async () => {
45+
searchContent = searchInput.value
1646

47+
chrome.tabs.sendMessage(currentTab.id, 'get body content text')
48+
})
49+
50+
chrome.runtime.onMessage.addListener(async (bodyContentText) => {
51+
const el = document.createElement('div')
52+
el.setAttribute('class', 'item')
53+
messageList.insertBefore(el, messageList.firstElementChild)
54+
55+
const reader = await fetchOpenAIStreamReader(searchContent, bodyContentText)
56+
await handleStreamReaderAnswer(el, reader)
57+
})
58+
}
59+
60+
async function fetchOpenAIStreamReader(searchContent, bodyContentText) {
1761
console.log('searchContent', searchContent)
18-
console.log('aiResponseMessage', aiResponseMessage)
62+
console.log('bodyContentText', bodyContentText)
1963

20-
div.innerText = aiResponseMessage
21-
messageList.insertBefore(div, messageList.firstElementChild)
22-
})
64+
const rule = `
65+
你需要基于名为page text提供的内容来回答名为 clien 的问题
2366
24-
async function fetchOpenAI(searchContent) {
25-
const bodyOptions = {
67+
1.接收输入:接收名为page text的内容(body.innerText)和名为clien的问题。
68+
2.预处理:对body.innerText进行文本清理、分词和词性标注。
69+
3.问题分类:将clien的问题进行分类,确定其类型(如事实性、观点性等)。
70+
4.上下文分析:在body.innerText中查找与问题相关的段落或句子,并分析它们的上下文信 息。
71+
5.答案抽取或生成:根据问题的类型和上下文信息,抽取相关的答案片段或生成合适的回答。
72+
6.返回回答:将生成的回答返回给用户。
73+
`
74+
75+
const Options = {
2676
model: 'gpt-3.5-turbo',
2777
messages: [
28-
{ role: 'system', content: '你需要根据内容回答用户问题' },
29-
{ role: 'user', content: searchContent }
78+
{ role: 'system', content: rule },
79+
{ role: 'user', name: 'page-text', content: bodyContentText },
80+
{ role: 'user', name: 'clien', content: searchContent }
3081
],
31-
temperature: 0.1
82+
stream: true
3283
}
3384

3485
try {
35-
const response = await fetch('', {
86+
const response = await fetch(`${config.BASE_URL}/chat/completions`, {
3687
headers: {
3788
'Content-Type': 'application/json',
38-
Authorization: `Bearer ${API_KEY}`
89+
Authorization: `Bearer ${config.API_KEY}`
3990
},
4091
method: 'post',
41-
body: JSON.stringify(bodyOptions)
92+
body: JSON.stringify(Options)
4293
})
4394

44-
const responseData = await response.json()
45-
46-
const result = responseData.choices[0].message.content
47-
48-
return result
95+
return response.body.getReader()
4996
} catch (error) {
50-
console.log(`error: ${error.message}`)
97+
console.log(`fetchOpenAIStreamReader error: ${error.message}`)
5198
}
5299
}
100+
101+
async function handleStreamReaderAnswer(el, reader) {
102+
const decoder = new TextDecoder()
103+
104+
return reader.read().then(function pump({ done, value }) {
105+
if (done) return
106+
107+
// 拿到当前切片的数据
108+
const text = decoder.decode(value)
109+
110+
const values = handleOpenAIChunkData(text)
111+
112+
values.forEach((itemText) => {
113+
const item = JSON.parse(itemText)
114+
115+
if (item.choices[0].finish_reason === 'stop') return
116+
117+
console.log(item)
118+
const content = item.choices[0].delta.content
119+
el.innerText += content
120+
})
121+
122+
return reader.read().then(pump)
123+
})
124+
}
125+
126+
function handleOpenAIChunkData(chunk) {
127+
const values = chunk
128+
.split('data: ')
129+
.filter((text) => text && !text.includes('DONE'))
130+
131+
console.log(values)
132+
133+
return values
134+
}

src/content.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
;(() => {
2+
chrome.runtime.onMessage.addListener(async (msg) => {
3+
console.log('content', msg)
4+
5+
const bodyContentText = document.body.innerText
6+
chrome.runtime.sendMessage(bodyContentText)
7+
})
8+
})()

0 commit comments

Comments
 (0)