Agent loop state machine refactor, unified LLM interface, and UI improvements
- Rewrite agent loop as Planning→Executing(N)→Completed state machine with per-step context isolation to prevent token explosion - Split tools and prompts by phase (planning vs execution) - Add advance_step/save_memo tools for step transitions and cross-step memory - Unify LLM interface: remove duplicate types, single chat_with_tools path - Add UTF-8 safe truncation (truncate_str) to prevent panics on Chinese text - Extract CreateForm component, add auto-scroll to execution log - Add report generation with app access URL, non-blocking title generation - Add timer system, file serving, app proxy, exec module - Update Dockerfile with uv, deployment config Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
142
web/src/components/CreateForm.vue
Normal file
142
web/src/components/CreateForm.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [requirement: string]
|
||||
cancel: []
|
||||
}>()
|
||||
|
||||
const requirement = ref('')
|
||||
const inputEl = ref<HTMLTextAreaElement>()
|
||||
|
||||
const examples = [
|
||||
{ label: 'Todo 应用', text: '做一个 Todo List 应用:前端展示任务列表(支持添加、完成、删除),后端 FastAPI 提供增删改查 REST API,数据存 SQLite。完成后用 curl 跑一遍 E2E 测试验证所有接口正常。' },
|
||||
{ label: '贪吃蛇+排行榜', text: '做一个贪吃蛇游戏网站,前端用 HTML/JS,后端用 FastAPI 存储排行榜,支持提交分数和查看 Top10' },
|
||||
{ label: '抓取豆瓣 Top250', text: '用 Python 抓取豆瓣电影 Top250 并生成分析报告' },
|
||||
]
|
||||
|
||||
onMounted(() => inputEl.value?.focus())
|
||||
|
||||
function onSubmit() {
|
||||
const text = requirement.value.trim()
|
||||
if (text) emit('submit', text)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="create-form">
|
||||
<h2>输入你的需求</h2>
|
||||
<div class="create-examples">
|
||||
<span
|
||||
v-for="ex in examples"
|
||||
:key="ex.label"
|
||||
class="example-tag"
|
||||
@click="requirement = ex.text"
|
||||
>{{ ex.label }}</span>
|
||||
</div>
|
||||
<textarea
|
||||
ref="inputEl"
|
||||
v-model="requirement"
|
||||
class="create-textarea"
|
||||
placeholder="描述你想让 AI 做什么..."
|
||||
rows="4"
|
||||
@keydown.ctrl.enter="onSubmit"
|
||||
@keydown.meta.enter="onSubmit"
|
||||
/>
|
||||
<div class="create-hint">Ctrl+Enter 提交</div>
|
||||
<div class="create-actions">
|
||||
<button class="btn-cancel" @click="emit('cancel')">取消</button>
|
||||
<button class="btn-confirm" @click="onSubmit" :disabled="!requirement.trim()">开始</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.create-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
width: 480px;
|
||||
}
|
||||
|
||||
.create-form h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.create-examples {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.example-tag {
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.example-tag:hover {
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
background: rgba(79, 195, 247, 0.08);
|
||||
}
|
||||
|
||||
.create-textarea {
|
||||
padding: 10px 12px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.create-textarea:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.create-hint {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.create-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 8px 16px;
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
padding: 8px 16px;
|
||||
background: var(--accent);
|
||||
color: var(--bg-primary);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-confirm:disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user