Implement missing GUI RFC contracts
This commit is contained in:
parent
85c0d294e2
commit
2343f2522a
15 changed files with 303 additions and 28 deletions
|
|
@ -11,6 +11,8 @@ type QueryModels struct{}
|
|||
|
||||
type QuerySettings struct{}
|
||||
|
||||
type QuerySettingsDefaults struct{}
|
||||
|
||||
type QueryConversationList struct{}
|
||||
|
||||
type QueryConversationGet struct {
|
||||
|
|
|
|||
|
|
@ -202,6 +202,8 @@ func (s *Service) handleQuery(_ *core.Core, q core.Query) core.Result {
|
|||
return core.Result{Value: s.discoverModels(), OK: true}
|
||||
case QuerySettings:
|
||||
return core.Result{Value: s.loadSettings(), OK: true}
|
||||
case QuerySettingsDefaults:
|
||||
return core.Result{Value: DefaultSettings(), OK: true}
|
||||
case QueryConversationList:
|
||||
conversations, err := s.listConversationSummaries()
|
||||
return core.Result{}.New(conversations, err)
|
||||
|
|
@ -264,6 +266,9 @@ func (s *Service) registerActions() {
|
|||
c.Action("gui.chat.settings.load", func(_ context.Context, _ core.Options) core.Result {
|
||||
return core.Result{Value: s.loadSettings(), OK: true}
|
||||
})
|
||||
c.Action("gui.chat.settings.defaults", func(_ context.Context, _ core.Options) core.Result {
|
||||
return core.Result{Value: DefaultSettings(), OK: true}
|
||||
})
|
||||
c.Action("gui.chat.settings.reset", func(_ context.Context, _ core.Options) core.Result {
|
||||
settings := DefaultSettings()
|
||||
err := s.saveSettings(settings)
|
||||
|
|
|
|||
|
|
@ -140,3 +140,18 @@ func TestService_Good_SelectModelUpdatesConversation(t *testing.T) {
|
|||
require.True(t, updated.OK)
|
||||
assert.Equal(t, "lemma", updated.Value.(Conversation).Model)
|
||||
}
|
||||
|
||||
func TestService_Good_SettingsDefaults(t *testing.T) {
|
||||
c := newChatCore(t, func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
_, _ = io.WriteString(w, "data: [DONE]\n\n")
|
||||
}, &mockToolExecutor{})
|
||||
|
||||
result := c.QUERY(QuerySettingsDefaults{})
|
||||
require.True(t, result.OK)
|
||||
assert.Equal(t, DefaultSettings(), result.Value.(ChatSettings))
|
||||
|
||||
actionResult := c.Action("gui.chat.settings.defaults").Run(context.Background(), core.Options{})
|
||||
require.True(t, actionResult.OK)
|
||||
assert.Equal(t, DefaultSettings(), actionResult.Value.(ChatSettings))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -292,7 +292,6 @@ func (em *WSEventManager) handleMessages(conn *websocket.Conn) {
|
|||
return
|
||||
}
|
||||
|
||||
handled := true
|
||||
switch msg.Action {
|
||||
case "subscribe":
|
||||
em.subscribe(conn, msg.ID, msg.EventTypes)
|
||||
|
|
@ -301,9 +300,6 @@ func (em *WSEventManager) handleMessages(conn *websocket.Conn) {
|
|||
case "list":
|
||||
em.listSubscriptions(conn)
|
||||
default:
|
||||
handled = false
|
||||
}
|
||||
if !handled {
|
||||
em.closeWithPolicyViolation(conn, "unknown websocket action")
|
||||
return
|
||||
}
|
||||
|
|
@ -319,7 +315,12 @@ func (em *WSEventManager) closeWithPolicyViolation(conn *websocket.Conn, reason
|
|||
}
|
||||
state.writeMu.Lock()
|
||||
defer state.writeMu.Unlock()
|
||||
_ = conn.WriteJSON(map[string]any{
|
||||
"error": reason,
|
||||
"status": websocket.ClosePolicyViolation,
|
||||
})
|
||||
_ = conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.ClosePolicyViolation, reason), time.Now().Add(2*time.Second))
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
// subscribe adds a subscription for a client.
|
||||
|
|
|
|||
|
|
@ -173,6 +173,10 @@ func TestWSEventManager_HandleWebSocket_ClosesOnMalformedMessage(t *testing.T) {
|
|||
|
||||
require.NoError(t, conn.WriteMessage(websocket.TextMessage, []byte(`{"action":`)))
|
||||
|
||||
payload := readJSONMessage(t, conn)
|
||||
assert.Equal(t, "invalid websocket message", payload["error"])
|
||||
assert.Equal(t, float64(websocket.ClosePolicyViolation), payload["status"])
|
||||
|
||||
_, _, err := conn.ReadMessage()
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
|
@ -184,6 +188,10 @@ func TestWSEventManager_HandleWebSocket_ClosesOnUnknownAction(t *testing.T) {
|
|||
|
||||
require.NoError(t, conn.WriteMessage(websocket.TextMessage, []byte(`{"action":"bogus"}`)))
|
||||
|
||||
payload := readJSONMessage(t, conn)
|
||||
assert.Equal(t, "unknown websocket action", payload["error"])
|
||||
assert.Equal(t, float64(websocket.ClosePolicyViolation), payload["status"])
|
||||
|
||||
_, _, err := conn.ReadMessage()
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,12 @@ package display
|
|||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var hlcrfSlotPattern = regexp.MustCompile(`\{\{\s*slot\s+"([^"]+)"\s*\}\}`)
|
||||
|
||||
func (s *Service) buildHLCRFComponents(pageURL string) string {
|
||||
loaded, err := s.loadManifestForOrigin(pageURL)
|
||||
if err != nil || loaded == nil {
|
||||
|
|
@ -33,6 +36,7 @@ func (s *Service) buildHLCRFComponents(pageURL string) string {
|
|||
}
|
||||
|
||||
func renderHLCRFComponent(tag, templateBody string) string {
|
||||
templateBody = compileHLCRFTemplate(templateBody)
|
||||
return `(function(){if(customElements.get(` + quoteJS(tag) + `)){return;}const tpl=document.createElement('template');tpl.innerHTML=` +
|
||||
quoteJS(templateBody) +
|
||||
`;class CoreHLCRFElement extends HTMLElement{connectedCallback(){if(this.shadowRoot){return;}const root=this.attachShadow({mode:'open'});root.appendChild(tpl.content.cloneNode(true));}}customElements.define(` +
|
||||
|
|
@ -40,6 +44,20 @@ func renderHLCRFComponent(tag, templateBody string) string {
|
|||
`,CoreHLCRFElement);})();`
|
||||
}
|
||||
|
||||
func compileHLCRFTemplate(templateBody string) string {
|
||||
return hlcrfSlotPattern.ReplaceAllStringFunc(templateBody, func(source string) string {
|
||||
match := hlcrfSlotPattern.FindStringSubmatch(source)
|
||||
if len(match) < 2 {
|
||||
return source
|
||||
}
|
||||
slotName := strings.TrimSpace(match[1])
|
||||
if slotName == "" || strings.EqualFold(slotName, "default") {
|
||||
return "<slot></slot>"
|
||||
}
|
||||
return `<slot name="` + slotName + `"></slot>`
|
||||
})
|
||||
}
|
||||
|
||||
func defaultHLCRFTag(name string) string {
|
||||
name = strings.TrimSpace(strings.ToLower(name))
|
||||
name = strings.TrimSuffix(name, filepath.Ext(name))
|
||||
|
|
|
|||
|
|
@ -46,6 +46,13 @@ func TestHLCRF_BuildHLCRFComponents_Good(t *testing.T) {
|
|||
assert.Contains(t, script, "core-inline")
|
||||
}
|
||||
|
||||
func TestHLCRF_CompileHLCRFTemplate_Good(t *testing.T) {
|
||||
compiled := compileHLCRFTemplate(`<section data-slot="H">{{slot "H"}}</section><main>{{ slot "L-C" }}</main>`)
|
||||
|
||||
assert.Contains(t, compiled, `<slot name="H"></slot>`)
|
||||
assert.Contains(t, compiled, `<slot name="L-C"></slot>`)
|
||||
}
|
||||
|
||||
func TestHLCRF_BuildHLCRFComponents_Bad(t *testing.T) {
|
||||
svc := &Service{}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,8 +80,12 @@ func (s *Service) resolveCoreRoute(ctx context.Context, route string, query url.
|
|||
return s.resolveModelsRoute(subpath, query)
|
||||
case "chat":
|
||||
return s.resolveChatRoute(ctx, subpath, query)
|
||||
case "agent", "wallet", "identity":
|
||||
return s.resolveUnavailableCoreRoute(segment, subpath, query)
|
||||
case "agent":
|
||||
return s.resolveServiceBackedCoreRoute("agent", subpath, query, "agent", "core-agent")
|
||||
case "wallet":
|
||||
return s.resolveServiceBackedCoreRoute("wallet", subpath, query, "wallet", "blockchain", "go-blockchain")
|
||||
case "identity":
|
||||
return s.resolveServiceBackedCoreRoute("identity", subpath, query, "identity", "tim", "TIM")
|
||||
default:
|
||||
return core.Result{
|
||||
Value: coreerr.E("display.resolveCoreRoute", "unknown core route: "+segment, nil),
|
||||
|
|
@ -238,6 +242,61 @@ func (s *Service) resolveUnavailableCoreRoute(route, subpath string, query url.V
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Service) resolveServiceBackedCoreRoute(route, subpath string, query url.Values, serviceNames ...string) core.Result {
|
||||
for _, serviceName := range serviceNames {
|
||||
serviceName = strings.TrimSpace(serviceName)
|
||||
if serviceName == "" {
|
||||
continue
|
||||
}
|
||||
serviceResult := s.Core().Service(serviceName)
|
||||
if !serviceResult.OK {
|
||||
continue
|
||||
}
|
||||
payload := map[string]any{
|
||||
"route": route,
|
||||
"service": serviceName,
|
||||
"subpath": subpath,
|
||||
"query": query,
|
||||
"value": serviceResult.Value,
|
||||
"actions": s.actionsForService(serviceName),
|
||||
"services": s.Core().Services(),
|
||||
}
|
||||
return core.Result{
|
||||
Value: map[string]any{
|
||||
"content_type": "text/html",
|
||||
"body": s.renderSchemeBody(route, payload),
|
||||
"route": route,
|
||||
"service": serviceName,
|
||||
"value": serviceResult.Value,
|
||||
},
|
||||
OK: true,
|
||||
}
|
||||
}
|
||||
return s.resolveUnavailableCoreRoute(route, subpath, query)
|
||||
}
|
||||
|
||||
func (s *Service) actionsForService(serviceName string) []string {
|
||||
if strings.TrimSpace(serviceName) == "" {
|
||||
return nil
|
||||
}
|
||||
prefixes := []string{
|
||||
serviceName + ".",
|
||||
"core." + serviceName + ".",
|
||||
"gui." + serviceName + ".",
|
||||
}
|
||||
actions := make([]string, 0)
|
||||
for _, actionName := range s.Core().Actions() {
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(actionName, prefix) {
|
||||
actions = append(actions, actionName)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(actions)
|
||||
return actions
|
||||
}
|
||||
|
||||
func (s *Service) currentSettingsSnapshot() map[string]any {
|
||||
if s.configFile != nil {
|
||||
var snapshot map[string]any
|
||||
|
|
|
|||
|
|
@ -172,6 +172,34 @@ func TestScheme_ResolveScheme_Ugly(t *testing.T) {
|
|||
assert.Contains(t, searchPayload["body"].(string), "No matches found in Core storage.")
|
||||
}
|
||||
|
||||
func TestScheme_ResolveScheme_ServiceBackedRoute_Good(t *testing.T) {
|
||||
c := core.New(
|
||||
core.WithService(Register(nil)),
|
||||
core.WithName("wallet", func(_ *core.Core) core.Result {
|
||||
return core.Result{
|
||||
Value: map[string]any{
|
||||
"balance": "42.0",
|
||||
"address": "lthn1example",
|
||||
},
|
||||
OK: true,
|
||||
}
|
||||
}),
|
||||
core.WithServiceLock(),
|
||||
)
|
||||
require.True(t, c.ServiceStartup(context.Background(), nil).OK)
|
||||
|
||||
svc := core.MustServiceFor[*Service](c, "display")
|
||||
svc.registerDefaultSchemes()
|
||||
|
||||
result := svc.ResolveScheme(context.Background(), "core://wallet/treasury?amount=1")
|
||||
require.True(t, result.OK)
|
||||
payload := result.Value.(map[string]any)
|
||||
assert.Equal(t, "wallet", payload["route"])
|
||||
assert.Equal(t, "wallet", payload["service"])
|
||||
assert.Contains(t, payload["body"].(string), "lthn1example")
|
||||
assert.Contains(t, payload["body"].(string), "42.0")
|
||||
}
|
||||
|
||||
func TestScheme_ResolveScheme_NetworkPeers_Good(t *testing.T) {
|
||||
c := core.New(
|
||||
core.WithService(Register(nil)),
|
||||
|
|
|
|||
|
|
@ -10,12 +10,7 @@ import (
|
|||
|
||||
func (s *Service) registerSidecarActions() {
|
||||
if strings.TrimSpace(core.Env("CORE_DENO_ENABLE")) != "" && s.sidecar == nil {
|
||||
manager := deno.New(deno.Options{
|
||||
Binary: strings.TrimSpace(core.Env("CORE_DENO_BINARY")),
|
||||
Dir: strings.TrimSpace(core.Env("CORE_DENO_DIR")),
|
||||
Args: splitCommandArgs(core.Env("CORE_DENO_ARGS")),
|
||||
})
|
||||
s.sidecar = manager
|
||||
s.sidecar = s.ensureSidecar()
|
||||
_, _ = s.sidecar.Start(context.Background())
|
||||
}
|
||||
|
||||
|
|
@ -32,6 +27,17 @@ func (s *Service) registerSidecarActions() {
|
|||
s.Core().Action("display.sidecar.status", func(_ context.Context, _ core.Options) core.Result {
|
||||
return core.Result{Value: s.ensureSidecar().Status(), OK: true}
|
||||
})
|
||||
s.Core().Action("core.deno.sidecar.start", func(ctx context.Context, _ core.Options) core.Result {
|
||||
status, err := s.ensureSidecar().Start(ctx)
|
||||
return core.Result{}.New(status, err)
|
||||
})
|
||||
s.Core().Action("core.deno.sidecar.stop", func(ctx context.Context, _ core.Options) core.Result {
|
||||
status, err := s.ensureSidecar().Stop(ctx)
|
||||
return core.Result{}.New(status, err)
|
||||
})
|
||||
s.Core().Action("core.deno.sidecar.status", func(_ context.Context, _ core.Options) core.Result {
|
||||
return core.Result{Value: s.ensureSidecar().Status(), OK: true}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) ensureSidecar() *deno.Manager {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
|
@ -105,6 +106,17 @@ func VerifyManifest(manifest Manifest) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (i Installer) Verify(ctx context.Context, manifestURL string) (Manifest, error) {
|
||||
manifest, err := i.FetchManifest(ctx, manifestURL)
|
||||
if err != nil {
|
||||
return Manifest{}, err
|
||||
}
|
||||
if err := VerifyManifest(manifest); err != nil {
|
||||
return Manifest{}, err
|
||||
}
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
func (i Installer) Install(ctx context.Context, manifest Manifest) (string, error) {
|
||||
if strings.TrimSpace(i.InstallDir) == "" {
|
||||
return "", errors.New("install dir is required")
|
||||
|
|
@ -148,9 +160,39 @@ func (i Installer) Install(ctx context.Context, manifest Manifest) (string, erro
|
|||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return "", fmt.Errorf("git clone failed: %w: %s", err, strings.TrimSpace(string(output)))
|
||||
}
|
||||
if err := writeInstalledManifest(targetDir, manifest); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return targetDir, nil
|
||||
}
|
||||
|
||||
func (i Installer) List(ctx context.Context, registryURL string) ([]Manifest, error) {
|
||||
client := i.HTTPClient
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, registryURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
return nil, fmt.Errorf("marketplace list failed: %s", resp.Status)
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, maxManifestBytes+1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(body) > maxManifestBytes {
|
||||
return nil, fmt.Errorf("marketplace list failed: payload exceeds %d bytes", maxManifestBytes)
|
||||
}
|
||||
return decodeManifestList(body)
|
||||
}
|
||||
|
||||
func validateManifestName(value string) error {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
|
|
@ -198,3 +240,39 @@ func safeName(value string) string {
|
|||
}
|
||||
return cleaned
|
||||
}
|
||||
|
||||
func decodeManifestList(body []byte) ([]Manifest, error) {
|
||||
trimmed := strings.TrimSpace(string(body))
|
||||
if trimmed == "" {
|
||||
return nil, nil
|
||||
}
|
||||
var manifests []Manifest
|
||||
if strings.HasPrefix(trimmed, "[") {
|
||||
if err := json.Unmarshal(body, &manifests); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return manifests, nil
|
||||
}
|
||||
var wrapped struct {
|
||||
Manifests []Manifest `json:"manifests" yaml:"manifests"`
|
||||
}
|
||||
if err := yaml.Unmarshal(body, &wrapped); err == nil && wrapped.Manifests != nil {
|
||||
return wrapped.Manifests, nil
|
||||
}
|
||||
if err := yaml.Unmarshal(body, &manifests); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return manifests, nil
|
||||
}
|
||||
|
||||
func writeInstalledManifest(targetDir string, manifest Manifest) error {
|
||||
manifestDir := filepath.Join(targetDir, ".core")
|
||||
if err := os.MkdirAll(manifestDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := yaml.Marshal(manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join(manifestDir, "marketplace.yaml"), data, 0o644)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func signedManifest(t *testing.T, manifest Manifest) Manifest {
|
||||
|
|
@ -152,6 +153,10 @@ func TestMarketplace_Install_Good(t *testing.T) {
|
|||
assert.Contains(t, string(contents), "clone")
|
||||
assert.Contains(t, string(contents), "--branch")
|
||||
assert.Contains(t, string(contents), "main")
|
||||
|
||||
installedManifest, err := os.ReadFile(filepath.Join(targetDir, ".core", "marketplace.yaml"))
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(installedManifest), "name: Core UI")
|
||||
}
|
||||
|
||||
func TestMarketplace_Install_Bad(t *testing.T) {
|
||||
|
|
@ -191,6 +196,43 @@ func TestMarketplace_Install_Ugly(t *testing.T) {
|
|||
assert.Contains(t, err.Error(), "git clone failed")
|
||||
}
|
||||
|
||||
func TestMarketplace_Verify_Good(t *testing.T) {
|
||||
manifest := signedManifest(t, Manifest{
|
||||
Name: "core-ui",
|
||||
Version: "1.2.3",
|
||||
Repository: "https://example.com/core-ui.git",
|
||||
Ref: "main",
|
||||
})
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
data, err := yaml.Marshal(manifest)
|
||||
require.NoError(t, err)
|
||||
_, _ = w.Write(data)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
installer := Installer{HTTPClient: server.Client()}
|
||||
verified, err := installer.Verify(context.Background(), server.URL)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, manifest.Name, verified.Name)
|
||||
assert.Equal(t, manifest.Ref, verified.Ref)
|
||||
}
|
||||
|
||||
func TestMarketplace_List_Good(t *testing.T) {
|
||||
manifests := []Manifest{
|
||||
{Name: "core-ui", Version: "1.2.3"},
|
||||
{Name: "core-chat", Version: "0.9.0"},
|
||||
}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
_, _ = w.Write([]byte("manifests:\n - name: core-ui\n version: 1.2.3\n - name: core-chat\n version: 0.9.0\n"))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
installer := Installer{HTTPClient: server.Client()}
|
||||
listed, err := installer.List(context.Background(), server.URL)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, manifests, listed)
|
||||
}
|
||||
|
||||
func shellQuote(value string) string {
|
||||
return "'" + strings.ReplaceAll(value, "'", "'\\''") + "'"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,12 +130,14 @@ export class ChatStateService {
|
|||
}
|
||||
|
||||
async resetSettings(): Promise<void> {
|
||||
const reset = await this.invoke('gui.chat.settings.reset');
|
||||
if (reset) {
|
||||
this.settings.set(reset);
|
||||
if (reset.default_model) {
|
||||
this.selectedModel.set(reset.default_model);
|
||||
}
|
||||
const defaults = await this.invoke('gui.chat.settings.defaults');
|
||||
if (!defaults) {
|
||||
return;
|
||||
}
|
||||
const saved = await this.invoke('gui.chat.settings.save', defaults);
|
||||
if (saved) {
|
||||
this.settings.set(saved);
|
||||
this.selectedModel.set(saved.default_model || '');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -479,6 +481,17 @@ export class ChatStateService {
|
|||
if (route === 'gui.chat.settings.load') {
|
||||
return this.settings() as T;
|
||||
}
|
||||
if (route === 'gui.chat.settings.defaults') {
|
||||
return {
|
||||
temperature: 1,
|
||||
top_p: 0.95,
|
||||
top_k: 64,
|
||||
max_tokens: 2048,
|
||||
context_window: 8192,
|
||||
system_prompt: 'You are a helpful assistant.',
|
||||
default_model: '',
|
||||
} as T;
|
||||
}
|
||||
if (route === 'gui.chat.settings.save') {
|
||||
const settings = payload as ChatSettings;
|
||||
this.settings.set(settings);
|
||||
|
|
@ -488,15 +501,7 @@ export class ChatStateService {
|
|||
return settings as T;
|
||||
}
|
||||
if (route === 'gui.chat.settings.reset') {
|
||||
const defaults: ChatSettings = {
|
||||
temperature: 1,
|
||||
top_p: 0.95,
|
||||
top_k: 64,
|
||||
max_tokens: 2048,
|
||||
context_window: 8192,
|
||||
system_prompt: 'You are a helpful assistant.',
|
||||
default_model: '',
|
||||
};
|
||||
const defaults = (await this.mockInvoke('gui.chat.settings.defaults')) as ChatSettings;
|
||||
this.settings.set(defaults);
|
||||
return defaults as T;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { ChatSettings, ModelEntry } from './chat.types';
|
|||
<header>
|
||||
<strong>Inference settings</strong>
|
||||
<div class="actions">
|
||||
<button type="button" (click)="reset.emit()">Reset</button>
|
||||
<button type="button" (click)="reset.emit()">Reset to defaults</button>
|
||||
<button type="button" (click)="closed.emit()">Close</button>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ declare global {
|
|||
|
||||
export interface ChatRouteMap {
|
||||
'gui.chat.models': { request: void; response: ModelEntry[] };
|
||||
'gui.chat.settings.defaults': { request: void; response: ChatSettings };
|
||||
'gui.chat.settings.load': { request: void; response: ChatSettings };
|
||||
'gui.chat.settings.save': { request: ChatSettings; response: ChatSettings };
|
||||
'gui.chat.settings.reset': { request: void; response: ChatSettings };
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue