| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- import { useState } from 'react'
- import { generateTopicTableQueries, getTopicTableQueryPrompt } from '../../api/workbench.js'
- const EXAMPLE = {
- topicBuildId: '1229',
- topicId: '1392',
- }
- function PromptModal({ payload, loading, error, onClose }) {
- return (
- <div className="manual-modal-mask" onClick={onClose}>
- <section className="manual-modal-panel topic-prompt-modal" onClick={(event) => event.stopPropagation()}>
- <header className="manual-modal-head">
- <div>
- <span className="topic-eyebrow">选题表信息 → 大模型判断 → 搜索词</span>
- <h2>生成 Prompt</h2>
- <p>{payload ? `${payload.name} · ${payload.version}` : '读取当前线上使用的完整 Prompt'}</p>
- </div>
- <button className="manual-close" type="button" onClick={onClose} aria-label="关闭">×</button>
- </header>
- {loading && <div className="topic-inline-state">正在读取 Prompt…</div>}
- {error && <div className="manual-error">{error}</div>}
- {payload && <pre className="topic-prompt-code">{payload.system_prompt}</pre>}
- </section>
- </div>
- )
- }
- function GenerationRows({ result }) {
- const rows = result.field_mappings || []
- return (
- <section className="topic-route-map">
- <div className="topic-route-map-head">
- <strong>选题表里用到的信息</strong>
- <strong>大模型判断什么</strong>
- <strong>生成哪些具体搜索词</strong>
- </div>
- <div className="topic-route-map-body">
- {rows.map((row) => (
- <article className="topic-route-row" key={row.field_key}>
- <div className="topic-route-input">
- <span>{row.field_label}</span>
- <code>{row.field_path}</code>
- <p>{row.field_value}</p>
- </div>
- <div className="topic-route-judgement">
- <p>{row.judgement}</p>
- </div>
- <div className="topic-route-queries">
- {row.queries.map((query, index) => (
- <div key={`${query}-${index}`}>
- <b>{index + 1}</b>
- <p>{query}</p>
- </div>
- ))}
- </div>
- </article>
- ))}
- </div>
- </section>
- )
- }
- export default function TopicTableQueryPanel() {
- const [topicBuildId, setTopicBuildId] = useState(EXAMPLE.topicBuildId)
- const [topicId, setTopicId] = useState(EXAMPLE.topicId)
- const [result, setResult] = useState(null)
- const [loading, setLoading] = useState(false)
- const [error, setError] = useState('')
- const [promptOpen, setPromptOpen] = useState(false)
- const [prompt, setPrompt] = useState(null)
- const [promptLoading, setPromptLoading] = useState(false)
- const [promptError, setPromptError] = useState('')
- const showPrompt = async () => {
- setPromptOpen(true)
- if (prompt || promptLoading) return
- setPromptLoading(true)
- setPromptError('')
- try {
- setPrompt(await getTopicTableQueryPrompt())
- } catch (e) {
- setPromptError(e.message || 'Prompt 读取失败')
- } finally {
- setPromptLoading(false)
- }
- }
- const generate = async (event) => {
- event.preventDefault()
- setLoading(true)
- setError('')
- try {
- const payload = {
- topic_build_id: Number(topicBuildId),
- topic_id: topicId ? Number(topicId) : null,
- max_queries: 18,
- }
- setResult(await generateTopicTableQueries(payload))
- } catch (e) {
- setError(e.message || '搜索词生成失败')
- } finally {
- setLoading(false)
- }
- }
- return (
- <div className="topic-table-panel">
- <header className="topic-table-intro">
- <div>
- <span className="topic-eyebrow">第四种生成方式</span>
- <h2>根据选题表生成搜索词</h2>
- <p>每一行都是一个真实字段,对应一项大模型判断和由它生成的搜索词。</p>
- </div>
- <span className="topic-preview-only">这里只生成搜索词,不会开始搜索</span>
- </header>
- <form className="topic-table-form" onSubmit={generate}>
- <label>
- <span>选题表编号</span>
- <input value={topicBuildId} onChange={(e) => setTopicBuildId(e.target.value)} type="number" min="1" required />
- </label>
- <label>
- <span>选题编号</span>
- <input value={topicId} onChange={(e) => setTopicId(e.target.value)} type="number" min="1" placeholder="不填则取 mature 选题" />
- </label>
- <button className="topic-example-button" type="button" onClick={() => {
- setTopicBuildId(EXAMPLE.topicBuildId)
- setTopicId(EXAMPLE.topicId)
- setResult(null)
- setError('')
- }}>载入真实示例 1229</button>
- <button className="topic-prompt-button" type="button" onClick={showPrompt}>查看生成 Prompt</button>
- <button className="topic-generate-button" type="submit" disabled={loading}>
- {loading ? '大模型正在生成…' : '生成搜索词'}
- </button>
- </form>
- {error && <div className="manual-error topic-generation-error">{error}</div>}
- {!result && !loading && (
- <section className="topic-example-map">
- <div className="topic-example-cell">
- <span>实质字段</span>
- <code>topics[id=1392].composition_items[id=25686].element_name</code>
- <p>彩虹光晕</p>
- </div>
- <div className="topic-example-cell">
- <span>大模型判断什么</span>
- <p>这个画面里的彩虹光晕,怎样才能稳定拍出来?</p>
- </div>
- <div className="topic-example-cell">
- <span>生成的搜索词</span>
- <ul>
- <li>逆光人像怎么拍出自然彩虹光晕</li>
- <li>低角度拍摄时太阳和镜头怎么配合</li>
- </ul>
- </div>
- </section>
- )}
- {loading && <div className="topic-loading-card"><span />正在读取真实字段,并逐字段生成搜索词…</div>}
- {result && <GenerationRows result={result} />}
- {promptOpen && (
- <PromptModal
- payload={prompt}
- loading={promptLoading}
- error={promptError}
- onClose={() => setPromptOpen(false)}
- />
- )}
- </div>
- )
- }
|