@make-u-free/migi 0.4.5 → 0.4.7
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/bin/migi.js +18 -10
- package/package.json +1 -1
- package/src/agent.js +52 -11
package/bin/migi.js
CHANGED
|
@@ -87,10 +87,18 @@ async function readChatInput() {
|
|
|
87
87
|
let curLine = 0
|
|
88
88
|
let drawnLines = 0
|
|
89
89
|
let cursorLine = 0 // カーソルの物理行(drawn area 先頭からの offset)
|
|
90
|
+
let drawPending = false
|
|
90
91
|
|
|
91
92
|
emitKeypressEvents(process.stdin)
|
|
92
93
|
if (process.stdin.isTTY) process.stdin.setRawMode(true)
|
|
93
94
|
|
|
95
|
+
// ペースト等の連続入力をまとめて1回の描画にするためのデバウンス
|
|
96
|
+
function scheduleDraw() {
|
|
97
|
+
if (drawPending) return
|
|
98
|
+
drawPending = true
|
|
99
|
+
setImmediate(() => { drawPending = false; draw() })
|
|
100
|
+
}
|
|
101
|
+
|
|
94
102
|
function draw() {
|
|
95
103
|
const w = process.stdout.columns || 80
|
|
96
104
|
const newLines = [
|
|
@@ -136,11 +144,12 @@ async function readChatInput() {
|
|
|
136
144
|
|
|
137
145
|
const onKey = (str, key) => {
|
|
138
146
|
if (!key) {
|
|
139
|
-
if (str) { lines[curLine] += str;
|
|
147
|
+
if (str) { lines[curLine] += str; scheduleDraw() }
|
|
140
148
|
return
|
|
141
149
|
}
|
|
142
150
|
|
|
143
151
|
if (key.ctrl && key.name === 'c') {
|
|
152
|
+
if (drawPending) { drawPending = false; draw() } // カーソル位置を確定させてから終了
|
|
144
153
|
process.stdout.write(`\x1b[${drawnLines - 1 - curLine}B\n`)
|
|
145
154
|
process.stdin.removeListener('keypress', onKey)
|
|
146
155
|
if (process.stdin.isTTY) process.stdin.setRawMode(false)
|
|
@@ -153,9 +162,10 @@ async function readChatInput() {
|
|
|
153
162
|
if (key.meta || key.shift) {
|
|
154
163
|
lines.splice(curLine + 1, 0, '')
|
|
155
164
|
curLine++
|
|
156
|
-
|
|
165
|
+
scheduleDraw()
|
|
157
166
|
} else {
|
|
158
|
-
// Enter →
|
|
167
|
+
// Enter → 送信(保留中の描画があれば先に確定)
|
|
168
|
+
if (drawPending) { drawPending = false; draw() }
|
|
159
169
|
const content = lines.join('\n').trim()
|
|
160
170
|
if (!content) return
|
|
161
171
|
process.stdout.write(`\x1b[${drawnLines - 1 - curLine}B\n`)
|
|
@@ -169,18 +179,18 @@ async function readChatInput() {
|
|
|
169
179
|
if (key.name === 'backspace') {
|
|
170
180
|
if (lines[curLine].length > 0) {
|
|
171
181
|
lines[curLine] = lines[curLine].slice(0, -1)
|
|
172
|
-
|
|
182
|
+
scheduleDraw()
|
|
173
183
|
} else if (curLine > 0) {
|
|
174
184
|
lines.splice(curLine, 1)
|
|
175
185
|
curLine--
|
|
176
|
-
|
|
186
|
+
scheduleDraw()
|
|
177
187
|
}
|
|
178
188
|
return
|
|
179
189
|
}
|
|
180
190
|
|
|
181
191
|
if (str && !key.ctrl && !key.meta) {
|
|
182
192
|
lines[curLine] += str
|
|
183
|
-
|
|
193
|
+
scheduleDraw()
|
|
184
194
|
}
|
|
185
195
|
}
|
|
186
196
|
|
|
@@ -239,8 +249,7 @@ async function prompt() {
|
|
|
239
249
|
console.log('\n' + sepWithLabel(chalk.bold.cyan(agentName) + chalk.dim(` [スキル: ${parsed.name}]`)))
|
|
240
250
|
const expanded = expandSkill(skill.content, parsed.args)
|
|
241
251
|
try {
|
|
242
|
-
|
|
243
|
-
console.log('\n' + reply + '\n')
|
|
252
|
+
await agent.chat(expanded)
|
|
244
253
|
} catch (err) {
|
|
245
254
|
console.error(chalk.red('\n エラー: ' + err.message + '\n'))
|
|
246
255
|
}
|
|
@@ -255,8 +264,7 @@ async function prompt() {
|
|
|
255
264
|
// --- 通常チャット ---
|
|
256
265
|
console.log('\n' + sepWithLabel(chalk.bold.cyan(agentName)))
|
|
257
266
|
try {
|
|
258
|
-
|
|
259
|
-
console.log('\n' + reply + '\n')
|
|
267
|
+
await agent.chat(input)
|
|
260
268
|
} catch (err) {
|
|
261
269
|
console.error(chalk.red('\n エラー: ' + err.message + '\n'))
|
|
262
270
|
}
|
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -97,28 +97,69 @@ ${userNameLine}
|
|
|
97
97
|
|
|
98
98
|
while (true) {
|
|
99
99
|
spinner.start('考え中…')
|
|
100
|
-
|
|
100
|
+
|
|
101
|
+
const stream = await this.client.chat.completions.create({
|
|
101
102
|
model: this.model,
|
|
102
103
|
messages,
|
|
103
104
|
tools: this.tools,
|
|
104
|
-
tool_choice: 'auto'
|
|
105
|
+
tool_choice: 'auto',
|
|
106
|
+
stream: true
|
|
105
107
|
})
|
|
106
|
-
spinner.stop()
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
let content = ''
|
|
110
|
+
const tcMap = {} // tool_calls をインデックスで蓄積
|
|
111
|
+
let finishReason = null
|
|
112
|
+
let streaming = false // 最初のコンテンツが届いたか
|
|
113
|
+
|
|
114
|
+
for await (const chunk of stream) {
|
|
115
|
+
const choice = chunk.choices[0]
|
|
116
|
+
if (!choice) continue
|
|
117
|
+
const delta = choice.delta
|
|
118
|
+
if (choice.finish_reason) finishReason = choice.finish_reason
|
|
119
|
+
|
|
120
|
+
// テキストチャンク
|
|
121
|
+
if (delta?.content) {
|
|
122
|
+
if (!streaming) {
|
|
123
|
+
spinner.stop()
|
|
124
|
+
process.stdout.write('\n')
|
|
125
|
+
streaming = true
|
|
126
|
+
}
|
|
127
|
+
content += delta.content
|
|
128
|
+
process.stdout.write(delta.content)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// tool_calls チャンク(引数はストリームで分割されて届く)
|
|
132
|
+
if (delta?.tool_calls) {
|
|
133
|
+
for (const tc of delta.tool_calls) {
|
|
134
|
+
if (!tcMap[tc.index]) tcMap[tc.index] = { id: '', type: 'function', function: { name: '', arguments: '' } }
|
|
135
|
+
if (tc.id) tcMap[tc.index].id += tc.id
|
|
136
|
+
if (tc.function?.name) tcMap[tc.index].function.name += tc.function.name
|
|
137
|
+
if (tc.function?.arguments) tcMap[tc.index].function.arguments += tc.function.arguments
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
spinner.stop()
|
|
111
143
|
|
|
112
144
|
// 通常の返答
|
|
113
|
-
if (
|
|
114
|
-
|
|
145
|
+
if (finishReason === 'stop') {
|
|
146
|
+
process.stdout.write('\n\n')
|
|
147
|
+
const assistantMsg = { role: 'assistant', content }
|
|
148
|
+
messages.push(assistantMsg)
|
|
149
|
+
this.history.push(assistantMsg)
|
|
150
|
+
return content
|
|
115
151
|
}
|
|
116
152
|
|
|
117
153
|
// ツール呼び出し
|
|
118
|
-
if (
|
|
119
|
-
|
|
154
|
+
if (finishReason === 'tool_calls') {
|
|
155
|
+
if (streaming) process.stdout.write('\n')
|
|
156
|
+
const toolCalls = Object.values(tcMap)
|
|
157
|
+
const assistantMsg = { role: 'assistant', content: content || null, tool_calls: toolCalls }
|
|
158
|
+
messages.push(assistantMsg)
|
|
159
|
+
this.history.push(assistantMsg)
|
|
120
160
|
|
|
121
|
-
|
|
161
|
+
const toolResults = []
|
|
162
|
+
for (const toolCall of toolCalls) {
|
|
122
163
|
const args = JSON.parse(toolCall.function.arguments)
|
|
123
164
|
const name = toolCall.function.name
|
|
124
165
|
|