您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

164 行
6.8KB

  1. {{define "settings"}}
  2. <!doctype html>
  3. <html lang="en">
  4. <head>
  5. <title>{{.Title}}</title>
  6. {{template "head" .}}
  7. </head>
  8. <body>
  9. {{template "nav" .}}
  10. {{if .Msg}}<div class="flash flash-ok">{{.Msg}}</div>{{end}}
  11. {{if .Err}}<div class="flash flash-err">{{.Err}}</div>{{end}}
  12. <h1>Settings</h1>
  13. <p>QC-Settings plus LLM- und globale Prompt-/Systemsteuerung fuer den spaeteren LLM-Flow.</p>
  14. <table>
  15. <tr><th>QC Base URL</th><td class="mono">{{.QCBaseURL}}</td></tr>
  16. <tr><th>Bearer token configured</th><td>{{if .TokenConfigured}}yes{{else}}no{{end}}</td></tr>
  17. <tr><th>Poll interval (seconds)</th><td>{{.PollIntervalSeconds}}</td></tr>
  18. <tr><th>Poll timeout (seconds)</th><td>{{.PollTimeoutSeconds}}</td></tr>
  19. <tr><th>Poll max concurrent</th><td>{{.PollMaxConcurrent}}</td></tr>
  20. <tr><th>Language output mode</th><td>{{.LanguageOutputMode}}</td></tr>
  21. </table>
  22. <h2>LLM Provider / Modell</h2>
  23. <p><small>Provider-/Modellwahl mit statischem provider-aware Katalog (spaeter erweiterbar um dynamisches Refresh), Runtime-Tuning und provider-spezifischen Keys.</small></p>
  24. <form method="post" action="/settings/llm">
  25. <div>
  26. <label>Provider
  27. <select id="llm-provider" name="llm_provider">
  28. {{range .LLMProviderOptions}}
  29. <option value="{{.Value}}" {{if eq $.LLMActiveProvider .Value}}selected{{end}}>{{.Label}}</option>
  30. {{end}}
  31. </select>
  32. </label>
  33. </div>
  34. <div>
  35. <label>Model
  36. <select id="llm-model" name="llm_model" data-selected="{{.LLMActiveModel}}">
  37. {{range .LLMModelOptions}}
  38. <option value="{{.Value}}" {{if eq $.LLMActiveModel .Value}}selected{{end}}>{{.Label}}</option>
  39. {{end}}
  40. </select>
  41. </label>
  42. </div>
  43. <div id="llm-base-url-wrap" {{if ne .LLMActiveProvider "ollama"}}style="display:none;"{{end}}>
  44. <label>Base URL (nur Ollama / kompatible Endpoints)
  45. <input type="url" name="llm_base_url" placeholder="http://localhost:11434/v1" value="{{.LLMBaseURL}}">
  46. </label>
  47. </div>
  48. <div>
  49. <label>Temperature (0.0 - 2.0)
  50. <input type="number" name="llm_temperature" min="0" max="2" step="0.01" value="{{printf "%.2f" .LLMTemperature}}">
  51. </label>
  52. </div>
  53. <div>
  54. <label>Max Tokens (64 - 8192)
  55. <input type="number" name="llm_max_tokens" min="64" max="8192" step="1" value="{{.LLMMaxTokens}}">
  56. </label>
  57. </div>
  58. <div>
  59. <label>OpenAI API Key ({{if .OpenAIKeyConfigured}}configured{{else}}not configured{{end}})
  60. <input type="password" name="llm_api_key_openai" placeholder="leer lassen = unveraendert">
  61. </label>
  62. </div>
  63. <div>
  64. <label>Anthropic API Key ({{if .AnthropicKeyConfigured}}configured{{else}}not configured{{end}})
  65. <input type="password" name="llm_api_key_anthropic" placeholder="leer lassen = unveraendert">
  66. </label>
  67. </div>
  68. <div>
  69. <label>Google API Key ({{if .GoogleKeyConfigured}}configured{{else}}not configured{{end}})
  70. <input type="password" name="llm_api_key_google" placeholder="leer lassen = unveraendert">
  71. </label>
  72. </div>
  73. <div>
  74. <label>xAI API Key ({{if .XAIKeyConfigured}}configured{{else}}not configured{{end}})
  75. <input type="password" name="llm_api_key_xai" placeholder="leer lassen = unveraendert">
  76. </label>
  77. </div>
  78. <div>
  79. <label>Ollama API Key (optional; {{if .OllamaKeyConfigured}}configured{{else}}not configured{{end}})
  80. <input type="password" name="llm_api_key_ollama" placeholder="leer lassen = unveraendert">
  81. </label>
  82. </div>
  83. <div>
  84. <label>Google Places API Key ({{if .GooglePlacesKeyConfigured}}configured{{else}}not configured{{end}})
  85. <input type="password" name="google_places_api_key" placeholder="leer lassen = unveraendert">
  86. <small>Wird fuer das Auto-Befuellen der Testimonials aus Google-Bewertungen verwendet.</small>
  87. </label>
  88. </div>
  89. <button type="submit" formaction="/settings/llm/validate">Validate provider config</button>
  90. <button type="submit">LLM-Settings speichern</button>
  91. </form>
  92. <h2>Globaler Master Prompt</h2>
  93. <p><small>Diese Einstellungen gelten systemweit und werden im normalen Build-/Review-Formular nicht mehr direkt editiert.</small></p>
  94. <form method="post" action="/settings/prompt">
  95. <input type="hidden" name="prompt_block_count" value="{{len .PromptBlocks}}">
  96. <div>
  97. <label>Master Prompt
  98. <textarea name="master_prompt">{{.MasterPrompt}}</textarea>
  99. </label>
  100. </div>
  101. <h3>Prompt-Bloecke (Standard)</h3>
  102. {{range $i, $block := .PromptBlocks}}
  103. <input type="hidden" name="prompt_block_id_{{$i}}" value="{{$block.ID}}">
  104. <div>
  105. <label>
  106. <input type="checkbox" name="prompt_block_enabled_{{$i}}" {{if $block.Enabled}}checked{{end}}>
  107. {{$block.Label}}
  108. </label>
  109. <input type="hidden" name="prompt_block_label_{{$i}}" value="{{$block.Label}}">
  110. <textarea name="prompt_block_instruction_{{$i}}">{{$block.Instruction}}</textarea>
  111. </div>
  112. {{end}}
  113. <button type="submit">Prompt-Settings speichern</button>
  114. </form>
  115. <script>
  116. (function () {
  117. var provider = document.getElementById('llm-provider');
  118. var model = document.getElementById('llm-model');
  119. var baseUrlWrap = document.getElementById('llm-base-url-wrap');
  120. if (!provider || !baseUrlWrap || !model) return;
  121. var modelCatalog = {
  122. {{range $provider := .LLMProviderOptions}}
  123. "{{$provider.Value}}": [
  124. {{range $idx, $model := $provider.Models}}{{if $idx}},{{end}}{"value":"{{$model.Value}}","label":"{{$model.Label}}"}{{end}}
  125. ],
  126. {{end}}
  127. };
  128. var selectedByProvider = {};
  129. selectedByProvider[provider.value] = model.dataset.selected || model.value;
  130. var syncModelOptions = function () {
  131. var providerValue = provider.value;
  132. var options = modelCatalog[providerValue] || [];
  133. var preferred = selectedByProvider[providerValue] || model.value;
  134. model.innerHTML = "";
  135. options.forEach(function (entry, idx) {
  136. var option = document.createElement('option');
  137. option.value = entry.value;
  138. option.textContent = entry.label;
  139. if (entry.value === preferred || (!preferred && idx === 0)) {
  140. option.selected = true;
  141. }
  142. model.appendChild(option);
  143. });
  144. };
  145. var syncBaseURLVisibility = function () {
  146. baseUrlWrap.style.display = provider.value === 'ollama' ? '' : 'none';
  147. };
  148. provider.addEventListener('change', function () {
  149. syncModelOptions();
  150. syncBaseURLVisibility();
  151. });
  152. model.addEventListener('change', function () {
  153. selectedByProvider[provider.value] = model.value;
  154. });
  155. syncModelOptions();
  156. syncBaseURLVisibility();
  157. })();
  158. </script>
  159. </body>
  160. </html>
  161. {{end}}