Add user defined system message
This commit is contained in:
@@ -12,7 +12,7 @@ llm:
|
|||||||
url: "http://localhost:8081/v1"
|
url: "http://localhost:8081/v1"
|
||||||
key: ${ODIDERE_LLM_KEY}
|
key: ${ODIDERE_LLM_KEY}
|
||||||
model: "default"
|
model: "default"
|
||||||
system_prompt: "You are a helpful voice assistant. Be concise."
|
system_message: "You are a helpful voice assistant. Be concise."
|
||||||
timeout: "5m"
|
timeout: "5m"
|
||||||
|
|
||||||
tts:
|
tts:
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ type Config struct {
|
|||||||
Key string `yaml:"key"`
|
Key string `yaml:"key"`
|
||||||
// Model is the model identifier.
|
// Model is the model identifier.
|
||||||
Model string `yaml:"model"`
|
Model string `yaml:"model"`
|
||||||
// SystemPrompt is prepended to all conversations.
|
// SystemMessage is prepended to all conversations.
|
||||||
SystemPrompt string `yaml:"system_prompt"`
|
SystemMessage string `yaml:"system_message"`
|
||||||
// Timeout is the maximum duration for a query (e.g., "5m").
|
// Timeout is the maximum duration for a query (e.g., "5m").
|
||||||
// Defaults to 5 minutes if empty.
|
// Defaults to 5 minutes if empty.
|
||||||
Timeout string `yaml:"timeout"`
|
Timeout string `yaml:"timeout"`
|
||||||
@@ -53,7 +53,7 @@ type Client struct {
|
|||||||
log *slog.Logger
|
log *slog.Logger
|
||||||
model string
|
model string
|
||||||
registry *tool.Registry
|
registry *tool.Registry
|
||||||
systemPrompt string
|
systemMessage string
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
tools []openai.Tool
|
tools []openai.Tool
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ func NewClient(
|
|||||||
llm := &Client{
|
llm := &Client{
|
||||||
log: log,
|
log: log,
|
||||||
model: cfg.Model,
|
model: cfg.Model,
|
||||||
systemPrompt: cfg.SystemPrompt,
|
systemMessage: cfg.SystemMessage,
|
||||||
registry: registry,
|
registry: registry,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,12 +122,14 @@ func (c *Client) DefaultModel() string {
|
|||||||
|
|
||||||
// Query sends messages to the LLM using the specified model.
|
// Query sends messages to the LLM using the specified model.
|
||||||
// If model is empty, uses the default configured model.
|
// If model is empty, uses the default configured model.
|
||||||
|
// If systemMessage is non-empty, it overrides the configured system message.
|
||||||
// Returns all messages generated during the query, including tool calls
|
// Returns all messages generated during the query, including tool calls
|
||||||
// and tool results. The final message is the last element in the slice.
|
// and tool results. The final message is the last element in the slice.
|
||||||
func (c *Client) Query(
|
func (c *Client) Query(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
messages []openai.ChatCompletionMessage,
|
messages []openai.ChatCompletionMessage,
|
||||||
model string,
|
model string,
|
||||||
|
systemMessage string,
|
||||||
) ([]openai.ChatCompletionMessage, error) {
|
) ([]openai.ChatCompletionMessage, error) {
|
||||||
ctx, cancel := context.WithTimeout(ctx, c.timeout)
|
ctx, cancel := context.WithTimeout(ctx, c.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -137,13 +139,19 @@ func (c *Client) Query(
|
|||||||
model = c.model
|
model = c.model
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepend system prompt, if configured and not already present.
|
// Use per-request system message if provided, otherwise fall back to config.
|
||||||
if c.systemPrompt != "" && (len(messages) == 0 ||
|
effectiveMessage := c.systemMessage
|
||||||
|
if systemMessage != "" {
|
||||||
|
effectiveMessage = systemMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend system message, if configured and not already present.
|
||||||
|
if effectiveMessage != "" && (len(messages) == 0 ||
|
||||||
messages[0].Role != openai.ChatMessageRoleSystem) {
|
messages[0].Role != openai.ChatMessageRoleSystem) {
|
||||||
messages = append(
|
messages = append(
|
||||||
[]openai.ChatCompletionMessage{{
|
[]openai.ChatCompletionMessage{{
|
||||||
Role: openai.ChatMessageRoleSystem,
|
Role: openai.ChatMessageRoleSystem,
|
||||||
Content: c.systemPrompt,
|
Content: effectiveMessage,
|
||||||
}},
|
}},
|
||||||
messages...,
|
messages...,
|
||||||
)
|
)
|
||||||
@@ -239,11 +247,13 @@ type StreamEvent struct {
|
|||||||
// streams results. Each complete message (assistant reply, tool call,
|
// streams results. Each complete message (assistant reply, tool call,
|
||||||
// tool result) is sent to the events channel as it becomes available.
|
// tool result) is sent to the events channel as it becomes available.
|
||||||
// The channel is closed before returning.
|
// The channel is closed before returning.
|
||||||
|
// If systemMessage is non-empty, it overrides the configured system message.
|
||||||
// Returns all messages generated during the query.
|
// Returns all messages generated during the query.
|
||||||
func (c *Client) QueryStream(
|
func (c *Client) QueryStream(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
messages []openai.ChatCompletionMessage,
|
messages []openai.ChatCompletionMessage,
|
||||||
model string,
|
model string,
|
||||||
|
systemMessage string,
|
||||||
events chan<- StreamEvent,
|
events chan<- StreamEvent,
|
||||||
) error {
|
) error {
|
||||||
defer close(events)
|
defer close(events)
|
||||||
@@ -256,13 +266,19 @@ func (c *Client) QueryStream(
|
|||||||
model = c.model
|
model = c.model
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepend system prompt, if configured and not already present.
|
// Use per-request system message if provided, otherwise fall back to config.
|
||||||
if c.systemPrompt != "" && (len(messages) == 0 ||
|
effectiveMessage := c.systemMessage
|
||||||
|
if systemMessage != "" {
|
||||||
|
effectiveMessage = systemMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend system message, if configured and not already present.
|
||||||
|
if effectiveMessage != "" && (len(messages) == 0 ||
|
||||||
messages[0].Role != openai.ChatMessageRoleSystem) {
|
messages[0].Role != openai.ChatMessageRoleSystem) {
|
||||||
messages = append(
|
messages = append(
|
||||||
[]openai.ChatCompletionMessage{{
|
[]openai.ChatCompletionMessage{{
|
||||||
Role: openai.ChatMessageRoleSystem,
|
Role: openai.ChatMessageRoleSystem,
|
||||||
Content: c.systemPrompt,
|
Content: effectiveMessage,
|
||||||
}},
|
}},
|
||||||
messages...,
|
messages...,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func TestConfigValidate(t *testing.T) {
|
|||||||
cfg: Config{
|
cfg: Config{
|
||||||
Key: "sk-test-key",
|
Key: "sk-test-key",
|
||||||
Model: "test-model",
|
Model: "test-model",
|
||||||
SystemPrompt: "You are a helpful assistant.",
|
SystemMessage: "You are a helpful assistant.",
|
||||||
Timeout: "30m",
|
Timeout: "30m",
|
||||||
URL: "http://localhost:8080",
|
URL: "http://localhost:8080",
|
||||||
},
|
},
|
||||||
@@ -123,7 +123,7 @@ func TestNewClient(t *testing.T) {
|
|||||||
cfg: Config{
|
cfg: Config{
|
||||||
Key: "test-key",
|
Key: "test-key",
|
||||||
Model: "test-model",
|
Model: "test-model",
|
||||||
SystemPrompt: "Test prompt",
|
SystemMessage: "Test message",
|
||||||
Timeout: "5m",
|
Timeout: "5m",
|
||||||
URL: "http://localhost:8080",
|
URL: "http://localhost:8080",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -311,6 +311,8 @@ type Request struct {
|
|||||||
Messages []openai.ChatCompletionMessage `json:"messages"`
|
Messages []openai.ChatCompletionMessage `json:"messages"`
|
||||||
// Model is the LLM model ID. If empty, the default model is used.
|
// Model is the LLM model ID. If empty, the default model is used.
|
||||||
Model string `json:"model,omitempty"`
|
Model string `json:"model,omitempty"`
|
||||||
|
// SystemMessage overrides the configured system message for this request.
|
||||||
|
SystemMessage string `json:"system_message,omitempty"`
|
||||||
// Voice is the voice ID for TTS.
|
// Voice is the voice ID for TTS.
|
||||||
Voice string `json:"voice,omitempty"`
|
Voice string `json:"voice,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -455,7 +457,7 @@ func (svc *Service) voice(w http.ResponseWriter, r *http.Request) {
|
|||||||
if model == "" {
|
if model == "" {
|
||||||
model = svc.llm.DefaultModel()
|
model = svc.llm.DefaultModel()
|
||||||
}
|
}
|
||||||
msgs, err := svc.llm.Query(ctx, messages, model)
|
msgs, err := svc.llm.Query(ctx, messages, model, req.SystemMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorContext(
|
log.ErrorContext(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -731,7 +733,7 @@ func (svc *Service) voiceStream(w http.ResponseWriter, r *http.Request) {
|
|||||||
llmErr error
|
llmErr error
|
||||||
)
|
)
|
||||||
go func() {
|
go func() {
|
||||||
llmErr = svc.llm.QueryStream(ctx, messages, model, events)
|
llmErr = svc.llm.QueryStream(ctx, messages, model, req.SystemMessage, events)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Consume events and send as SSE.
|
// Consume events and send as SSE.
|
||||||
|
|||||||
@@ -90,4 +90,9 @@
|
|||||||
<path d="m15 9-6 6"/>
|
<path d="m15 9-6 6"/>
|
||||||
<path d="m9 9 6 6"/>
|
<path d="m9 9 6 6"/>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
||||||
|
<symbol id="settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/>
|
||||||
|
<circle cx="12" cy="12" r="3"/>
|
||||||
|
</symbol>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.9 KiB |
@@ -593,6 +593,201 @@ body {
|
|||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================== */
|
||||||
|
/* Settings Modal */
|
||||||
|
/* ==================== */
|
||||||
|
|
||||||
|
.settings-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1000;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-overlay.open {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel {
|
||||||
|
background: var(--color-surface);
|
||||||
|
border: 1px solid var(--color-border-light);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 480px;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: var(--s1);
|
||||||
|
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: var(--s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel__title {
|
||||||
|
font-size: var(--s1);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close button: remove toolbar border, darken the X. */
|
||||||
|
#settings-close {
|
||||||
|
border: none;
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
#settings-close:hover {
|
||||||
|
border-color: transparent;
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabs */
|
||||||
|
.settings-tabs {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 2px solid var(--color-border-light);
|
||||||
|
margin-bottom: var(--s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab {
|
||||||
|
padding: var(--s-2) var(--s-1);
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: var(--s-1);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.settings-tab:hover {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab:focus-visible {
|
||||||
|
outline: 2px solid var(--color-primary);
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab.active {
|
||||||
|
color: var(--color-text);
|
||||||
|
border-bottom-color: var(--color-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab panels */
|
||||||
|
.settings-tab-panel {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab-panel.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-label {
|
||||||
|
display: block;
|
||||||
|
font-size: var(--s-1);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text);
|
||||||
|
margin-bottom: var(--s-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-select {
|
||||||
|
width: 100%;
|
||||||
|
height: var(--s2);
|
||||||
|
padding: 0 var(--s-1);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: var(--s-1);
|
||||||
|
line-height: var(--s2);
|
||||||
|
color: var(--color-text);
|
||||||
|
background: var(--color-surface);
|
||||||
|
border: 1px solid var(--color-border-light);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
cursor: pointer;
|
||||||
|
margin-bottom: var(--s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-select:focus-visible {
|
||||||
|
outline: 2px solid var(--color-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-textarea {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
min-height: calc(var(--s2) * 6);
|
||||||
|
padding: var(--s-1);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--s-1);
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--color-text);
|
||||||
|
background: var(--color-bg);
|
||||||
|
border: 1px solid var(--color-border-light);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
resize: vertical;
|
||||||
|
margin-bottom: var(--s-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-textarea:focus {
|
||||||
|
outline: 2px solid var(--color-primary);
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-save-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-save-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--s-2) var(--s-1);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: var(--s-1);
|
||||||
|
font-weight: 500;
|
||||||
|
color: white;
|
||||||
|
background: var(--color-primary);
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.15s ease;
|
||||||
|
min-width: var(--s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.settings-save-btn:hover {
|
||||||
|
background: var(--color-primary-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-save-btn:focus-visible {
|
||||||
|
outline: 2px solid var(--color-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-save-btn--success {
|
||||||
|
background: var(--color-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-save-btn .icon {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* ==================== */
|
/* ==================== */
|
||||||
/* Animations */
|
/* Animations */
|
||||||
/* ==================== */
|
/* ==================== */
|
||||||
@@ -668,4 +863,16 @@ body {
|
|||||||
max-width: 128px;
|
max-width: 128px;
|
||||||
font-size: var(--s-1);
|
font-size: var(--s-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-overlay {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100dvh;
|
||||||
|
height: 100dvh;
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const ICONS_URL = '/static/icons.svg';
|
|||||||
const MODELS_ENDPOINT = '/v1/models';
|
const MODELS_ENDPOINT = '/v1/models';
|
||||||
const MODEL_KEY = 'odidere_model';
|
const MODEL_KEY = 'odidere_model';
|
||||||
const STORAGE_KEY = 'odidere_history';
|
const STORAGE_KEY = 'odidere_history';
|
||||||
|
const SYSTEM_MESSAGE_KEY = 'odidere_system_message';
|
||||||
const VOICES_ENDPOINT = '/v1/voices';
|
const VOICES_ENDPOINT = '/v1/voices';
|
||||||
const VOICE_KEY = 'odidere_voice';
|
const VOICE_KEY = 'odidere_voice';
|
||||||
|
|
||||||
@@ -42,6 +43,15 @@ class Odidere {
|
|||||||
this.$voice = document.getElementById('voice');
|
this.$voice = document.getElementById('voice');
|
||||||
this.$mute = document.getElementById('mute');
|
this.$mute = document.getElementById('mute');
|
||||||
|
|
||||||
|
// Settings modal
|
||||||
|
this.$settings = document.getElementById('settings');
|
||||||
|
this.$settingsOverlay = document.getElementById('settings-overlay');
|
||||||
|
this.$settingsClose = document.getElementById('settings-close');
|
||||||
|
this.$settingsTabs = document.querySelectorAll('.settings-tab');
|
||||||
|
this.$settingsPanels = document.querySelectorAll('.settings-tab-panel');
|
||||||
|
this.$systemMessageInput = document.getElementById('system-message-input');
|
||||||
|
this.$saveSystemMessage = document.getElementById('save-system-message');
|
||||||
|
|
||||||
// Templates
|
// Templates
|
||||||
this.$tplAssistantMessage = document.getElementById(
|
this.$tplAssistantMessage = document.getElementById(
|
||||||
'tpl-assistant-message',
|
'tpl-assistant-message',
|
||||||
@@ -151,6 +161,28 @@ class Odidere {
|
|||||||
});
|
});
|
||||||
// Mute button
|
// Mute button
|
||||||
this.$mute.addEventListener('click', () => this.#toggleMute());
|
this.$mute.addEventListener('click', () => this.#toggleMute());
|
||||||
|
|
||||||
|
// Settings modal
|
||||||
|
this.$settings.addEventListener('click', () => this.openSettings());
|
||||||
|
this.$settingsClose.addEventListener('click', () => this.closeSettings());
|
||||||
|
this.$settingsOverlay.addEventListener('click', (e) => {
|
||||||
|
if (e.target === this.$settingsOverlay) this.closeSettings();
|
||||||
|
});
|
||||||
|
this.document.addEventListener('keydown', (e) => {
|
||||||
|
if (
|
||||||
|
e.key === 'Escape' &&
|
||||||
|
this.$settingsOverlay.classList.contains('open')
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.closeSettings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.$settingsTabs.forEach(($tab) => {
|
||||||
|
$tab.addEventListener('click', () => this.#switchTab($tab.dataset.tab));
|
||||||
|
});
|
||||||
|
this.$saveSystemMessage.addEventListener('click', () =>
|
||||||
|
this.#saveSystemMessage(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -557,6 +589,10 @@ class Odidere {
|
|||||||
voice: this.$voice.value,
|
voice: this.$voice.value,
|
||||||
model: this.$model.value,
|
model: this.$model.value,
|
||||||
};
|
};
|
||||||
|
const systemMessage = localStorage.getItem(SYSTEM_MESSAGE_KEY);
|
||||||
|
if (systemMessage) {
|
||||||
|
payload.system_message = systemMessage;
|
||||||
|
}
|
||||||
if (audio) {
|
if (audio) {
|
||||||
payload.audio = await this.#toBase64(audio);
|
payload.audio = await this.#toBase64(audio);
|
||||||
}
|
}
|
||||||
@@ -674,7 +710,11 @@ class Odidere {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Collect tool results and render once all have arrived.
|
// Collect tool results and render once all have arrived.
|
||||||
if (message.role === 'tool' && pendingTools && pendingTools.assistant) {
|
if (
|
||||||
|
message.role === 'tool' &&
|
||||||
|
pendingTools &&
|
||||||
|
pendingTools.assistant
|
||||||
|
) {
|
||||||
pendingTools.results.push(message);
|
pendingTools.results.push(message);
|
||||||
// Add to history (server needs it) but don't render yet.
|
// Add to history (server needs it) but don't render yet.
|
||||||
this.#appendHistory([message]);
|
this.#appendHistory([message]);
|
||||||
@@ -849,6 +889,67 @@ class Odidere {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* openSettings opens the settings modal and loads current values.
|
||||||
|
*/
|
||||||
|
openSettings() {
|
||||||
|
this.$settingsOverlay.classList.add('open');
|
||||||
|
this.#loadSystemMessage();
|
||||||
|
this.#switchTab('model-voice');
|
||||||
|
// Focus the close button for accessibility.
|
||||||
|
this.$settingsClose.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* closeSettings closes the settings modal and restores focus.
|
||||||
|
*/
|
||||||
|
closeSettings() {
|
||||||
|
this.$settingsOverlay.classList.remove('open');
|
||||||
|
this.$settings.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #switchTab switches the active tab in the settings modal.
|
||||||
|
* @param {string} tabName
|
||||||
|
*/
|
||||||
|
#switchTab(tabName) {
|
||||||
|
this.$settingsTabs.forEach(($tab) => {
|
||||||
|
const isActive = $tab.dataset.tab === tabName;
|
||||||
|
$tab.classList.toggle('active', isActive);
|
||||||
|
$tab.setAttribute('aria-selected', String(isActive));
|
||||||
|
});
|
||||||
|
this.$settingsPanels.forEach(($panel) => {
|
||||||
|
const isActive = $panel.dataset.tabPanel === tabName;
|
||||||
|
$panel.classList.toggle('active', isActive);
|
||||||
|
$panel.hidden = !isActive;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #saveSystemMessage saves the textarea value to localStorage.
|
||||||
|
*/
|
||||||
|
#saveSystemMessage() {
|
||||||
|
const value = this.$systemMessageInput.value;
|
||||||
|
localStorage.setItem(SYSTEM_MESSAGE_KEY, value);
|
||||||
|
|
||||||
|
// Brief visual feedback on the save button.
|
||||||
|
this.$saveSystemMessage.replaceChildren(this.#icon('check'));
|
||||||
|
this.$saveSystemMessage.classList.add('settings-save-btn--success');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$saveSystemMessage.textContent = 'Save';
|
||||||
|
this.$saveSystemMessage.classList.remove('settings-save-btn--success');
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #loadSystemMessage reads from localStorage and populates the textarea.
|
||||||
|
*/
|
||||||
|
#loadSystemMessage() {
|
||||||
|
const stored = localStorage.getItem(SYSTEM_MESSAGE_KEY);
|
||||||
|
this.$systemMessageInput.value = stored || '';
|
||||||
|
}
|
||||||
|
|
||||||
// ====================
|
// ====================
|
||||||
// RENDER: SELECTS
|
// RENDER: SELECTS
|
||||||
// ====================
|
// ====================
|
||||||
|
|||||||
@@ -2,5 +2,6 @@
|
|||||||
<body>
|
<body>
|
||||||
{{ template "main" . }}
|
{{ template "main" . }}
|
||||||
{{ template "templates" . }}
|
{{ template "templates" . }}
|
||||||
|
{{ template "modal/settings" . }}
|
||||||
</body>
|
</body>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
{{ define "footer/toolbar" }}
|
{{ define "footer/toolbar" }}
|
||||||
<div class="footer__toolbar">
|
<div class="footer__toolbar">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="footer__toolbar-btn"
|
||||||
|
id="settings"
|
||||||
|
aria-label="Settings"
|
||||||
|
>
|
||||||
|
<svg class="icon"><use href="/static/icons.svg#settings"></use></svg>
|
||||||
|
</button>
|
||||||
|
<div class="footer__toolbar-spacer"></div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="footer__toolbar-btn"
|
class="footer__toolbar-btn"
|
||||||
@@ -8,17 +17,6 @@
|
|||||||
>
|
>
|
||||||
<svg class="icon"><use href="/static/icons.svg#reset"></use></svg>
|
<svg class="icon"><use href="/static/icons.svg#reset"></use></svg>
|
||||||
</button>
|
</button>
|
||||||
<div class="footer__toolbar-spacer"></div>
|
|
||||||
<select id="model" class="footer__select" aria-label="Model">
|
|
||||||
<option value="" disabled selected>
|
|
||||||
Loading...
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<select id="voice" class="footer__select" aria-label="Voice">
|
|
||||||
<option value="" disabled selected>
|
|
||||||
Loading...
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="footer__toolbar-btn"
|
class="footer__toolbar-btn"
|
||||||
|
|||||||
83
internal/service/templates/static/modal/settings.gohtml
Normal file
83
internal/service/templates/static/modal/settings.gohtml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
{{ define "modal/settings" }}
|
||||||
|
<div class="settings-overlay" id="settings-overlay">
|
||||||
|
<div class="settings-panel" role="dialog" aria-modal="true"
|
||||||
|
aria-labelledby="settings-title">
|
||||||
|
<div class="settings-panel__header">
|
||||||
|
<h2 class="settings-panel__title" id="settings-title">Settings</h2>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="footer__toolbar-btn"
|
||||||
|
id="settings-close"
|
||||||
|
aria-label="Close settings"
|
||||||
|
>
|
||||||
|
<svg class="icon"><use href="/static/icons.svg#close"></use></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-tabs" role="tablist">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="settings-tab active"
|
||||||
|
role="tab"
|
||||||
|
data-tab="model-voice"
|
||||||
|
aria-selected="true"
|
||||||
|
aria-controls="panel-model-voice"
|
||||||
|
>Model & Voice</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="settings-tab"
|
||||||
|
role="tab"
|
||||||
|
data-tab="system-message"
|
||||||
|
aria-selected="false"
|
||||||
|
aria-controls="panel-system-message"
|
||||||
|
>System Message</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-tab-panels">
|
||||||
|
<div
|
||||||
|
class="settings-tab-panel active"
|
||||||
|
role="tabpanel"
|
||||||
|
data-tab-panel="model-voice"
|
||||||
|
id="panel-model-voice"
|
||||||
|
aria-labelledby="tab-model-voice"
|
||||||
|
>
|
||||||
|
<label class="settings-label" for="model">Model</label>
|
||||||
|
<select id="model" class="settings-select" aria-label="Model">
|
||||||
|
<option value="" disabled selected>Loading...</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label class="settings-label" for="voice">Voice</label>
|
||||||
|
<select id="voice" class="settings-select" aria-label="Voice">
|
||||||
|
<option value="" disabled selected>Loading...</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="settings-tab-panel"
|
||||||
|
role="tabpanel"
|
||||||
|
data-tab-panel="system-message"
|
||||||
|
id="panel-system-message"
|
||||||
|
aria-labelledby="tab-system-message"
|
||||||
|
hidden
|
||||||
|
>
|
||||||
|
<label class="settings-label" for="system-message-input">
|
||||||
|
System Message
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="system-message-input"
|
||||||
|
class="settings-textarea"
|
||||||
|
rows="8"
|
||||||
|
placeholder="Enter system message..."
|
||||||
|
></textarea>
|
||||||
|
<div class="settings-save-row">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="settings-save-btn"
|
||||||
|
id="save-system-message"
|
||||||
|
>Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
1
vendor/github.com/google/uuid/node_js.go
generated
vendored
1
vendor/github.com/google/uuid/node_js.go
generated
vendored
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build js
|
||||||
// +build js
|
// +build js
|
||||||
|
|
||||||
package uuid
|
package uuid
|
||||||
|
|||||||
1
vendor/github.com/google/uuid/node_net.go
generated
vendored
1
vendor/github.com/google/uuid/node_net.go
generated
vendored
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !js
|
||||||
// +build !js
|
// +build !js
|
||||||
|
|
||||||
package uuid
|
package uuid
|
||||||
|
|||||||
1
vendor/github.com/google/uuid/null.go
generated
vendored
1
vendor/github.com/google/uuid/null.go
generated
vendored
@@ -25,7 +25,6 @@ var jsonNull = []byte("null")
|
|||||||
// } else {
|
// } else {
|
||||||
// // NULL value
|
// // NULL value
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
type NullUUID struct {
|
type NullUUID struct {
|
||||||
UUID UUID
|
UUID UUID
|
||||||
Valid bool // Valid is true if UUID is not NULL
|
Valid bool // Valid is true if UUID is not NULL
|
||||||
|
|||||||
2
vendor/github.com/google/uuid/uuid.go
generated
vendored
2
vendor/github.com/google/uuid/uuid.go
generated
vendored
@@ -187,10 +187,12 @@ func Must(uuid UUID, err error) UUID {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate returns an error if s is not a properly formatted UUID in one of the following formats:
|
// Validate returns an error if s is not a properly formatted UUID in one of the following formats:
|
||||||
|
//
|
||||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
||||||
|
//
|
||||||
// It returns an error if the format is invalid, otherwise nil.
|
// It returns an error if the format is invalid, otherwise nil.
|
||||||
func Validate(s string) error {
|
func Validate(s string) error {
|
||||||
switch len(s) {
|
switch len(s) {
|
||||||
|
|||||||
1
vendor/gopkg.in/yaml.v3/emitterc.go
generated
vendored
1
vendor/gopkg.in/yaml.v3/emitterc.go
generated
vendored
@@ -165,7 +165,6 @@ func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
|||||||
// - 1 event for DOCUMENT-START
|
// - 1 event for DOCUMENT-START
|
||||||
// - 2 events for SEQUENCE-START
|
// - 2 events for SEQUENCE-START
|
||||||
// - 3 events for MAPPING-START
|
// - 3 events for MAPPING-START
|
||||||
//
|
|
||||||
func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool {
|
func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool {
|
||||||
if emitter.events_head == len(emitter.events) {
|
if emitter.events_head == len(emitter.events) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
50
vendor/gopkg.in/yaml.v3/parserc.go
generated
vendored
50
vendor/gopkg.in/yaml.v3/parserc.go
generated
vendored
@@ -227,6 +227,7 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool
|
|||||||
|
|
||||||
// Parse the production:
|
// Parse the production:
|
||||||
// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
|
// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
|
||||||
|
//
|
||||||
// ************
|
// ************
|
||||||
func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool {
|
func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
@@ -249,8 +250,11 @@ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t)
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// implicit_document ::= block_node DOCUMENT-END*
|
// implicit_document ::= block_node DOCUMENT-END*
|
||||||
|
//
|
||||||
// *
|
// *
|
||||||
|
//
|
||||||
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
||||||
|
//
|
||||||
// *************************
|
// *************************
|
||||||
func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool {
|
func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool {
|
||||||
|
|
||||||
@@ -356,8 +360,8 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
||||||
// ***********
|
|
||||||
//
|
//
|
||||||
|
// ***********
|
||||||
func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool {
|
func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
if token == nil {
|
if token == nil {
|
||||||
@@ -379,9 +383,10 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// implicit_document ::= block_node DOCUMENT-END*
|
// implicit_document ::= block_node DOCUMENT-END*
|
||||||
// *************
|
|
||||||
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
|
||||||
//
|
//
|
||||||
|
// *************
|
||||||
|
//
|
||||||
|
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
||||||
func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool {
|
func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
if token == nil {
|
if token == nil {
|
||||||
@@ -428,29 +433,40 @@ func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t)
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// block_node_or_indentless_sequence ::=
|
// block_node_or_indentless_sequence ::=
|
||||||
|
//
|
||||||
// ALIAS
|
// ALIAS
|
||||||
// *****
|
// *****
|
||||||
// | properties (block_content | indentless_block_sequence)?
|
// | properties (block_content | indentless_block_sequence)?
|
||||||
// ********** *
|
// ********** *
|
||||||
// | block_content | indentless_block_sequence
|
// | block_content | indentless_block_sequence
|
||||||
// *
|
// *
|
||||||
|
//
|
||||||
// block_node ::= ALIAS
|
// block_node ::= ALIAS
|
||||||
|
//
|
||||||
// *****
|
// *****
|
||||||
// | properties block_content?
|
// | properties block_content?
|
||||||
// ********** *
|
// ********** *
|
||||||
// | block_content
|
// | block_content
|
||||||
// *
|
// *
|
||||||
|
//
|
||||||
// flow_node ::= ALIAS
|
// flow_node ::= ALIAS
|
||||||
|
//
|
||||||
// *****
|
// *****
|
||||||
// | properties flow_content?
|
// | properties flow_content?
|
||||||
// ********** *
|
// ********** *
|
||||||
// | flow_content
|
// | flow_content
|
||||||
// *
|
// *
|
||||||
|
//
|
||||||
// properties ::= TAG ANCHOR? | ANCHOR TAG?
|
// properties ::= TAG ANCHOR? | ANCHOR TAG?
|
||||||
|
//
|
||||||
// *************************
|
// *************************
|
||||||
|
//
|
||||||
// block_content ::= block_collection | flow_collection | SCALAR
|
// block_content ::= block_collection | flow_collection | SCALAR
|
||||||
|
//
|
||||||
// ******
|
// ******
|
||||||
|
//
|
||||||
// flow_content ::= flow_collection | SCALAR
|
// flow_content ::= flow_collection | SCALAR
|
||||||
|
//
|
||||||
// ******
|
// ******
|
||||||
func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool {
|
func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool {
|
||||||
//defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)()
|
//defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)()
|
||||||
@@ -682,8 +698,8 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
|
// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
|
||||||
// ******************** *********** * *********
|
|
||||||
//
|
//
|
||||||
|
// ******************** *********** * *********
|
||||||
func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
||||||
if first {
|
if first {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
@@ -740,6 +756,7 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// indentless_sequence ::= (BLOCK-ENTRY block_node?)+
|
// indentless_sequence ::= (BLOCK-ENTRY block_node?)+
|
||||||
|
//
|
||||||
// *********** *
|
// *********** *
|
||||||
func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool {
|
func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
@@ -805,6 +822,7 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// block_mapping ::= BLOCK-MAPPING_START
|
// block_mapping ::= BLOCK-MAPPING_START
|
||||||
|
//
|
||||||
// *******************
|
// *******************
|
||||||
// ((KEY block_node_or_indentless_sequence?)?
|
// ((KEY block_node_or_indentless_sequence?)?
|
||||||
// *** *
|
// *** *
|
||||||
@@ -812,7 +830,6 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
|
|||||||
//
|
//
|
||||||
// BLOCK-END
|
// BLOCK-END
|
||||||
// *********
|
// *********
|
||||||
//
|
|
||||||
func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
||||||
if first {
|
if first {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
@@ -886,8 +903,6 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even
|
|||||||
// (VALUE block_node_or_indentless_sequence?)?)*
|
// (VALUE block_node_or_indentless_sequence?)?)*
|
||||||
// ***** *
|
// ***** *
|
||||||
// BLOCK-END
|
// BLOCK-END
|
||||||
//
|
|
||||||
//
|
|
||||||
func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
|
func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
if token == nil {
|
if token == nil {
|
||||||
@@ -915,6 +930,7 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// flow_sequence ::= FLOW-SEQUENCE-START
|
// flow_sequence ::= FLOW-SEQUENCE-START
|
||||||
|
//
|
||||||
// *******************
|
// *******************
|
||||||
// (flow_sequence_entry FLOW-ENTRY)*
|
// (flow_sequence_entry FLOW-ENTRY)*
|
||||||
// * **********
|
// * **********
|
||||||
@@ -922,9 +938,10 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev
|
|||||||
// *
|
// *
|
||||||
// FLOW-SEQUENCE-END
|
// FLOW-SEQUENCE-END
|
||||||
// *****************
|
// *****************
|
||||||
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
|
||||||
// *
|
|
||||||
//
|
//
|
||||||
|
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
|
//
|
||||||
|
// *
|
||||||
func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
||||||
if first {
|
if first {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
@@ -987,11 +1004,10 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
// *** *
|
|
||||||
//
|
//
|
||||||
|
// *** *
|
||||||
func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool {
|
func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
if token == nil {
|
if token == nil {
|
||||||
@@ -1011,8 +1027,8 @@ func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, ev
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
// ***** *
|
|
||||||
//
|
//
|
||||||
|
// ***** *
|
||||||
func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
|
func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
if token == nil {
|
if token == nil {
|
||||||
@@ -1035,8 +1051,8 @@ func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t,
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
// *
|
|
||||||
//
|
//
|
||||||
|
// *
|
||||||
func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool {
|
func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
if token == nil {
|
if token == nil {
|
||||||
@@ -1053,6 +1069,7 @@ func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, ev
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// flow_mapping ::= FLOW-MAPPING-START
|
// flow_mapping ::= FLOW-MAPPING-START
|
||||||
|
//
|
||||||
// ******************
|
// ******************
|
||||||
// (flow_mapping_entry FLOW-ENTRY)*
|
// (flow_mapping_entry FLOW-ENTRY)*
|
||||||
// * **********
|
// * **********
|
||||||
@@ -1060,9 +1077,9 @@ func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, ev
|
|||||||
// ******************
|
// ******************
|
||||||
// FLOW-MAPPING-END
|
// FLOW-MAPPING-END
|
||||||
// ****************
|
// ****************
|
||||||
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
|
||||||
// * *** *
|
|
||||||
//
|
//
|
||||||
|
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
|
// - *** *
|
||||||
func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
|
||||||
if first {
|
if first {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
@@ -1128,8 +1145,7 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event
|
|||||||
|
|
||||||
// Parse the productions:
|
// Parse the productions:
|
||||||
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||||
// * ***** *
|
// - ***** *
|
||||||
//
|
|
||||||
func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool {
|
func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool {
|
||||||
token := peek_token(parser)
|
token := peek_token(parser)
|
||||||
if token == nil {
|
if token == nil {
|
||||||
|
|||||||
8
vendor/gopkg.in/yaml.v3/scannerc.go
generated
vendored
8
vendor/gopkg.in/yaml.v3/scannerc.go
generated
vendored
@@ -1614,11 +1614,11 @@ func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
|
|||||||
// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token.
|
// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token.
|
||||||
//
|
//
|
||||||
// Scope:
|
// Scope:
|
||||||
|
//
|
||||||
// %YAML 1.1 # a comment \n
|
// %YAML 1.1 # a comment \n
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
// %TAG !yaml! tag:yaml.org,2002: \n
|
// %TAG !yaml! tag:yaml.org,2002: \n
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
//
|
|
||||||
func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool {
|
func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool {
|
||||||
// Eat '%'.
|
// Eat '%'.
|
||||||
start_mark := parser.mark
|
start_mark := parser.mark
|
||||||
@@ -1719,11 +1719,11 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool
|
|||||||
// Scan the directive name.
|
// Scan the directive name.
|
||||||
//
|
//
|
||||||
// Scope:
|
// Scope:
|
||||||
|
//
|
||||||
// %YAML 1.1 # a comment \n
|
// %YAML 1.1 # a comment \n
|
||||||
// ^^^^
|
// ^^^^
|
||||||
// %TAG !yaml! tag:yaml.org,2002: \n
|
// %TAG !yaml! tag:yaml.org,2002: \n
|
||||||
// ^^^
|
// ^^^
|
||||||
//
|
|
||||||
func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool {
|
func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool {
|
||||||
// Consume the directive name.
|
// Consume the directive name.
|
||||||
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
|
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
|
||||||
@@ -1758,6 +1758,7 @@ func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark
|
|||||||
// Scan the value of VERSION-DIRECTIVE.
|
// Scan the value of VERSION-DIRECTIVE.
|
||||||
//
|
//
|
||||||
// Scope:
|
// Scope:
|
||||||
|
//
|
||||||
// %YAML 1.1 # a comment \n
|
// %YAML 1.1 # a comment \n
|
||||||
// ^^^^^^
|
// ^^^^^^
|
||||||
func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool {
|
func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool {
|
||||||
@@ -1797,6 +1798,7 @@ const max_number_length = 2
|
|||||||
// Scan the version number of VERSION-DIRECTIVE.
|
// Scan the version number of VERSION-DIRECTIVE.
|
||||||
//
|
//
|
||||||
// Scope:
|
// Scope:
|
||||||
|
//
|
||||||
// %YAML 1.1 # a comment \n
|
// %YAML 1.1 # a comment \n
|
||||||
// ^
|
// ^
|
||||||
// %YAML 1.1 # a comment \n
|
// %YAML 1.1 # a comment \n
|
||||||
@@ -1834,9 +1836,9 @@ func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark
|
|||||||
// Scan the value of a TAG-DIRECTIVE token.
|
// Scan the value of a TAG-DIRECTIVE token.
|
||||||
//
|
//
|
||||||
// Scope:
|
// Scope:
|
||||||
|
//
|
||||||
// %TAG !yaml! tag:yaml.org,2002: \n
|
// %TAG !yaml! tag:yaml.org,2002: \n
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
//
|
|
||||||
func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool {
|
func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool {
|
||||||
var handle_value, prefix_value []byte
|
var handle_value, prefix_value []byte
|
||||||
|
|
||||||
|
|||||||
5
vendor/gopkg.in/yaml.v3/yaml.go
generated
vendored
5
vendor/gopkg.in/yaml.v3/yaml.go
generated
vendored
@@ -18,7 +18,6 @@
|
|||||||
// Source code and other details for the project are available at GitHub:
|
// Source code and other details for the project are available at GitHub:
|
||||||
//
|
//
|
||||||
// https://github.com/go-yaml/yaml
|
// https://github.com/go-yaml/yaml
|
||||||
//
|
|
||||||
package yaml
|
package yaml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -84,7 +83,6 @@ type Marshaler interface {
|
|||||||
//
|
//
|
||||||
// See the documentation of Marshal for the format of tags and a list of
|
// See the documentation of Marshal for the format of tags and a list of
|
||||||
// supported tag options.
|
// supported tag options.
|
||||||
//
|
|
||||||
func Unmarshal(in []byte, out interface{}) (err error) {
|
func Unmarshal(in []byte, out interface{}) (err error) {
|
||||||
return unmarshal(in, out, false)
|
return unmarshal(in, out, false)
|
||||||
}
|
}
|
||||||
@@ -214,7 +212,6 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) {
|
|||||||
// }
|
// }
|
||||||
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
|
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
|
||||||
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
|
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
|
||||||
//
|
|
||||||
func Marshal(in interface{}) (out []byte, err error) {
|
func Marshal(in interface{}) (out []byte, err error) {
|
||||||
defer handleErr(&err)
|
defer handleErr(&err)
|
||||||
e := newEncoder()
|
e := newEncoder()
|
||||||
@@ -368,7 +365,6 @@ const (
|
|||||||
//
|
//
|
||||||
// var person Node
|
// var person Node
|
||||||
// err := yaml.Unmarshal(data, &person)
|
// err := yaml.Unmarshal(data, &person)
|
||||||
//
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
// Kind defines whether the node is a document, a mapping, a sequence,
|
// Kind defines whether the node is a document, a mapping, a sequence,
|
||||||
// a scalar value, or an alias to another node. The specific data type of
|
// a scalar value, or an alias to another node. The specific data type of
|
||||||
@@ -421,7 +417,6 @@ func (n *Node) IsZero() bool {
|
|||||||
n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
|
n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// LongTag returns the long form of the tag that indicates the data type for
|
// LongTag returns the long form of the tag that indicates the data type for
|
||||||
// the node. If the Tag field isn't explicitly defined, one will be computed
|
// the node. If the Tag field isn't explicitly defined, one will be computed
|
||||||
// based on the node properties.
|
// based on the node properties.
|
||||||
|
|||||||
6
vendor/gopkg.in/yaml.v3/yamlh.go
generated
vendored
6
vendor/gopkg.in/yaml.v3/yamlh.go
generated
vendored
@@ -438,7 +438,9 @@ type yaml_document_t struct {
|
|||||||
// The number of written bytes should be set to the size_read variable.
|
// The number of written bytes should be set to the size_read variable.
|
||||||
//
|
//
|
||||||
// [in,out] data A pointer to an application data specified by
|
// [in,out] data A pointer to an application data specified by
|
||||||
|
//
|
||||||
// yaml_parser_set_input().
|
// yaml_parser_set_input().
|
||||||
|
//
|
||||||
// [out] buffer The buffer to write the data from the source.
|
// [out] buffer The buffer to write the data from the source.
|
||||||
// [in] size The size of the buffer.
|
// [in] size The size of the buffer.
|
||||||
// [out] size_read The actual number of bytes read from the source.
|
// [out] size_read The actual number of bytes read from the source.
|
||||||
@@ -639,7 +641,6 @@ type yaml_parser_t struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type yaml_comment_t struct {
|
type yaml_comment_t struct {
|
||||||
|
|
||||||
scan_mark yaml_mark_t // Position where scanning for comments started
|
scan_mark yaml_mark_t // Position where scanning for comments started
|
||||||
token_mark yaml_mark_t // Position after which tokens will be associated with this comment
|
token_mark yaml_mark_t // Position after which tokens will be associated with this comment
|
||||||
start_mark yaml_mark_t // Position of '#' comment mark
|
start_mark yaml_mark_t // Position of '#' comment mark
|
||||||
@@ -659,13 +660,14 @@ type yaml_comment_t struct {
|
|||||||
// @a buffer to the output.
|
// @a buffer to the output.
|
||||||
//
|
//
|
||||||
// @param[in,out] data A pointer to an application data specified by
|
// @param[in,out] data A pointer to an application data specified by
|
||||||
|
//
|
||||||
// yaml_emitter_set_output().
|
// yaml_emitter_set_output().
|
||||||
|
//
|
||||||
// @param[in] buffer The buffer with bytes to be written.
|
// @param[in] buffer The buffer with bytes to be written.
|
||||||
// @param[in] size The size of the buffer.
|
// @param[in] size The size of the buffer.
|
||||||
//
|
//
|
||||||
// @returns On success, the handler should return @c 1. If the handler failed,
|
// @returns On success, the handler should return @c 1. If the handler failed,
|
||||||
// the returned value should be @c 0.
|
// the returned value should be @c 0.
|
||||||
//
|
|
||||||
type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
|
type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
|
||||||
|
|
||||||
type yaml_emitter_state_t int
|
type yaml_emitter_state_t int
|
||||||
|
|||||||
Reference in New Issue
Block a user