fix refresh and max_tokens bug
This commit is contained in:
@@ -10,12 +10,18 @@ default_provider = "databricks"
|
|||||||
host = "https://your-workspace.cloud.databricks.com"
|
host = "https://your-workspace.cloud.databricks.com"
|
||||||
# token = "your-databricks-token" # Optional - will use OAuth if not provided
|
# token = "your-databricks-token" # Optional - will use OAuth if not provided
|
||||||
model = "databricks-claude-sonnet-4"
|
model = "databricks-claude-sonnet-4"
|
||||||
max_tokens = 4096
|
max_tokens = 4096 # Per-request output limit (how many tokens the model can generate per response)
|
||||||
|
# Note: This is different from max_context_length (total conversation history size)
|
||||||
temperature = 0.1
|
temperature = 0.1
|
||||||
use_oauth = true
|
use_oauth = true
|
||||||
|
|
||||||
[agent]
|
[agent]
|
||||||
fallback_default_max_tokens = 8192
|
fallback_default_max_tokens = 8192
|
||||||
|
# max_context_length: Override the context window size for all providers
|
||||||
|
# This is the total size of conversation history, not per-request output limit
|
||||||
|
# Useful for models with large context windows (e.g., Claude with 200k tokens)
|
||||||
|
# If not set, uses provider-specific defaults based on model capabilities
|
||||||
|
# max_context_length = 200000
|
||||||
enable_streaming = true
|
enable_streaming = true
|
||||||
timeout_seconds = 60
|
timeout_seconds = 60
|
||||||
# Retry configuration for recoverable errors (timeouts, rate limits, etc.)
|
# Retry configuration for recoverable errors (timeouts, rate limits, etc.)
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ pub struct EmbeddedConfig {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct AgentConfig {
|
pub struct AgentConfig {
|
||||||
|
pub max_context_length: Option<u32>,
|
||||||
pub fallback_default_max_tokens: usize,
|
pub fallback_default_max_tokens: usize,
|
||||||
pub enable_streaming: bool,
|
pub enable_streaming: bool,
|
||||||
pub timeout_seconds: u64,
|
pub timeout_seconds: u64,
|
||||||
@@ -135,6 +136,7 @@ impl Default for Config {
|
|||||||
player: None, // Will use default_provider if not specified
|
player: None, // Will use default_provider if not specified
|
||||||
},
|
},
|
||||||
agent: AgentConfig {
|
agent: AgentConfig {
|
||||||
|
max_context_length: None,
|
||||||
fallback_default_max_tokens: 8192,
|
fallback_default_max_tokens: 8192,
|
||||||
enable_streaming: true,
|
enable_streaming: true,
|
||||||
timeout_seconds: 60,
|
timeout_seconds: 60,
|
||||||
@@ -253,6 +255,7 @@ impl Config {
|
|||||||
player: None, // Will use default_provider if not specified
|
player: None, // Will use default_provider if not specified
|
||||||
},
|
},
|
||||||
agent: AgentConfig {
|
agent: AgentConfig {
|
||||||
|
max_context_length: None,
|
||||||
fallback_default_max_tokens: 8192,
|
fallback_default_max_tokens: 8192,
|
||||||
enable_streaming: true,
|
enable_streaming: true,
|
||||||
timeout_seconds: 60,
|
timeout_seconds: 60,
|
||||||
|
|||||||
@@ -97,6 +97,15 @@ const router = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we already have a container for instances
|
||||||
|
let instancesList = container.querySelector('.instances-list');
|
||||||
|
const isInitialLoad = !instancesList;
|
||||||
|
|
||||||
|
if (isInitialLoad) {
|
||||||
|
instancesList = document.createElement('div');
|
||||||
|
instancesList.className = 'instances-list';
|
||||||
|
}
|
||||||
|
|
||||||
if (instances.length === 0) {
|
if (instances.length === 0) {
|
||||||
console.log('[Router] No instances, showing empty state');
|
console.log('[Router] No instances, showing empty state');
|
||||||
container.innerHTML = components.emptyState(
|
container.innerHTML = components.emptyState(
|
||||||
@@ -104,15 +113,51 @@ const router = {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log('[Router] Building HTML for', instances.length, 'instances');
|
console.log('[Router] Building HTML for', instances.length, 'instances');
|
||||||
let html = '<div class="instances-list">';
|
|
||||||
for (const instance of instances) {
|
|
||||||
const stats = instance.stats || { total_tokens: 0, tool_calls: 0, errors: 0, duration_secs: 0 };
|
|
||||||
html += components.instancePanel(instance, stats, instance.latest_message);
|
|
||||||
}
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
console.log('[Router] Setting innerHTML (', html.length, 'chars)');
|
// Build a map of existing panels for efficient lookup
|
||||||
container.innerHTML = html;
|
const existingPanels = new Map();
|
||||||
|
if (!isInitialLoad) {
|
||||||
|
instancesList.querySelectorAll('.instance-panel').forEach(panel => {
|
||||||
|
const id = panel.getAttribute('data-id');
|
||||||
|
if (id) existingPanels.set(id, panel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track which IDs we've seen
|
||||||
|
const currentIds = new Set();
|
||||||
|
|
||||||
|
for (const instance of instances) {
|
||||||
|
currentIds.add(instance.id);
|
||||||
|
const stats = instance.stats || { total_tokens: 0, tool_calls: 0, errors: 0, duration_secs: 0 };
|
||||||
|
const newHtml = components.instancePanel(instance, stats, instance.latest_message);
|
||||||
|
|
||||||
|
const existingPanel = existingPanels.get(instance.id);
|
||||||
|
if (existingPanel) {
|
||||||
|
// Update existing panel in-place
|
||||||
|
const tempDiv = document.createElement('div');
|
||||||
|
tempDiv.innerHTML = newHtml;
|
||||||
|
const newPanel = tempDiv.firstElementChild;
|
||||||
|
existingPanel.replaceWith(newPanel);
|
||||||
|
} else {
|
||||||
|
// Add new panel
|
||||||
|
const tempDiv = document.createElement('div');
|
||||||
|
tempDiv.innerHTML = newHtml;
|
||||||
|
instancesList.appendChild(tempDiv.firstElementChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove panels for instances that no longer exist
|
||||||
|
existingPanels.forEach((panel, id) => {
|
||||||
|
if (!currentIds.has(id)) {
|
||||||
|
panel.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isInitialLoad) {
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.appendChild(instancesList);
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[Router] HTML set successfully');
|
console.log('[Router] HTML set successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +182,14 @@ const router = {
|
|||||||
console.log('[Router] renderDetail called for', id);
|
console.log('[Router] renderDetail called for', id);
|
||||||
|
|
||||||
this.currentInstanceId = id;
|
this.currentInstanceId = id;
|
||||||
|
|
||||||
|
// Check if we already have a detail view for this instance
|
||||||
|
let detailView = container.querySelector('.detail-view');
|
||||||
|
const isInitialLoad = !detailView || detailView.getAttribute('data-instance-id') !== id;
|
||||||
|
|
||||||
|
if (isInitialLoad) {
|
||||||
container.innerHTML = components.spinner('Loading instance details...');
|
container.innerHTML = components.spinner('Loading instance details...');
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const instance = await api.getInstance(id);
|
const instance = await api.getInstance(id);
|
||||||
@@ -149,9 +201,24 @@ const router = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If not initial load, update in place
|
||||||
|
if (!isInitialLoad) {
|
||||||
|
detailView = container.querySelector('.detail-view');
|
||||||
|
if (detailView) {
|
||||||
|
this.updateDetailView(detailView, instance, logs);
|
||||||
|
// Schedule next refresh
|
||||||
|
if (this.currentRoute === `/instance/${id}`) {
|
||||||
|
this.detailRefreshTimeout = setTimeout(() => {
|
||||||
|
this.renderDetail(container, id);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build detail view HTML
|
// Build detail view HTML
|
||||||
let html = `
|
let html = `
|
||||||
<div class="detail-view">
|
<div class="detail-view" data-instance-id="${id}">
|
||||||
<div class="detail-header">
|
<div class="detail-header">
|
||||||
<button class="btn btn-secondary" onclick="window.router.navigate('/')">← Back</button>
|
<button class="btn btn-secondary" onclick="window.router.navigate('/')">← Back</button>
|
||||||
<h2>${instance.workspace}</h2>
|
<h2>${instance.workspace}</h2>
|
||||||
@@ -159,19 +226,19 @@ const router = {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="detail-stats">
|
<div class="detail-stats">
|
||||||
<div class="stat-card">
|
<div class="stat-card" data-stat="tokens">
|
||||||
<div class="stat-label">Tokens</div>
|
<div class="stat-label">Tokens</div>
|
||||||
<div class="stat-value">${(instance.stats?.total_tokens || 0).toLocaleString()}</div>
|
<div class="stat-value">${(instance.stats?.total_tokens || 0).toLocaleString()}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card" data-stat="tool_calls">
|
||||||
<div class="stat-label">Tool Calls</div>
|
<div class="stat-label">Tool Calls</div>
|
||||||
<div class="stat-value">${instance.stats?.tool_calls || 0}</div>
|
<div class="stat-value">${instance.stats?.tool_calls || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card" data-stat="errors">
|
||||||
<div class="stat-label">Errors</div>
|
<div class="stat-label">Errors</div>
|
||||||
<div class="stat-value">${instance.stats?.errors || 0}</div>
|
<div class="stat-value">${instance.stats?.errors || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card" data-stat="duration">
|
||||||
<div class="stat-label">Duration</div>
|
<div class="stat-label">Duration</div>
|
||||||
<div class="stat-value">${Math.round((instance.stats?.duration_secs || 0) / 60)}m</div>
|
<div class="stat-value">${Math.round((instance.stats?.duration_secs || 0) / 60)}m</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -179,17 +246,17 @@ const router = {
|
|||||||
|
|
||||||
<div class="detail-section">
|
<div class="detail-section">
|
||||||
<h3>Git Status</h3>
|
<h3>Git Status</h3>
|
||||||
${components.gitStatus(instance.git_status)}
|
<div class="git-status-container">${components.gitStatus(instance.git_status)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="detail-section">
|
<div class="detail-section">
|
||||||
<h3>Project Files</h3>
|
<h3>Project Files</h3>
|
||||||
${components.projectFiles(instance.project_files)}
|
<div class="project-files-container">${components.projectFiles(instance.project_files)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
<h3>Tool Calls</h3>
|
<h3>Tool Calls</h3>
|
||||||
<div class="tool-calls-section">
|
<div class="tool-calls-section" data-section="tool-calls">
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Render tool calls
|
// Render tool calls
|
||||||
@@ -241,6 +308,105 @@ const router = {
|
|||||||
console.error('[Router] Error in renderDetail:', error);
|
console.error('[Router] Error in renderDetail:', error);
|
||||||
container.innerHTML = components.error('Failed to load instance: ' + error.message);
|
container.innerHTML = components.error('Failed to load instance: ' + error.message);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateDetailView(detailView, instance, logs) {
|
||||||
|
// Update status badge
|
||||||
|
const statusBadge = detailView.querySelector('.detail-header .badge');
|
||||||
|
if (statusBadge) {
|
||||||
|
const tempDiv = document.createElement('div');
|
||||||
|
tempDiv.innerHTML = components.statusBadge(instance.status);
|
||||||
|
statusBadge.replaceWith(tempDiv.firstElementChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
const tokensStat = detailView.querySelector('[data-stat="tokens"] .stat-value');
|
||||||
|
if (tokensStat) {
|
||||||
|
tokensStat.textContent = (instance.stats?.total_tokens || 0).toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolCallsStat = detailView.querySelector('[data-stat="tool_calls"] .stat-value');
|
||||||
|
if (toolCallsStat) {
|
||||||
|
toolCallsStat.textContent = instance.stats?.tool_calls || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorsStat = detailView.querySelector('[data-stat="errors"] .stat-value');
|
||||||
|
if (errorsStat) {
|
||||||
|
errorsStat.textContent = instance.stats?.errors || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const durationStat = detailView.querySelector('[data-stat="duration"] .stat-value');
|
||||||
|
if (durationStat) {
|
||||||
|
durationStat.textContent = Math.round((instance.stats?.duration_secs || 0) / 60) + 'm';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update git status
|
||||||
|
const gitStatusContainer = detailView.querySelector('.git-status-container');
|
||||||
|
if (gitStatusContainer) {
|
||||||
|
gitStatusContainer.innerHTML = components.gitStatus(instance.git_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update project files
|
||||||
|
const projectFilesContainer = detailView.querySelector('.project-files-container');
|
||||||
|
if (projectFilesContainer) {
|
||||||
|
projectFilesContainer.innerHTML = components.projectFiles(instance.project_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update tool calls
|
||||||
|
const toolCallsSection = detailView.querySelector('[data-section="tool-calls"]');
|
||||||
|
if (toolCallsSection && logs && logs.tool_calls) {
|
||||||
|
// Build a map of existing tool calls
|
||||||
|
const existingToolCalls = new Map();
|
||||||
|
toolCallsSection.querySelectorAll('.tool-call').forEach(tc => {
|
||||||
|
const id = tc.getAttribute('data-tool-id');
|
||||||
|
if (id) existingToolCalls.set(id, tc);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Track which IDs we've seen
|
||||||
|
const currentIds = new Set();
|
||||||
|
|
||||||
|
if (logs.tool_calls.length > 0) {
|
||||||
|
for (const toolCall of logs.tool_calls) {
|
||||||
|
currentIds.add(toolCall.id);
|
||||||
|
const newHtml = components.toolCall(toolCall);
|
||||||
|
|
||||||
|
const existingToolCall = existingToolCalls.get(toolCall.id);
|
||||||
|
if (existingToolCall) {
|
||||||
|
// Update existing tool call in-place
|
||||||
|
const tempDiv = document.createElement('div');
|
||||||
|
tempDiv.innerHTML = newHtml;
|
||||||
|
existingToolCall.replaceWith(tempDiv.firstElementChild);
|
||||||
|
} else {
|
||||||
|
// Add new tool call
|
||||||
|
const tempDiv = document.createElement('div');
|
||||||
|
tempDiv.innerHTML = newHtml;
|
||||||
|
toolCallsSection.appendChild(tempDiv.firstElementChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove tool calls that no longer exist
|
||||||
|
existingToolCalls.forEach((tc, id) => {
|
||||||
|
if (!currentIds.has(id)) {
|
||||||
|
tc.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update chat messages
|
||||||
|
const chatMessages = detailView.querySelector('.chat-messages');
|
||||||
|
if (chatMessages && logs && logs.messages && logs.messages.length > 0) {
|
||||||
|
let html = '';
|
||||||
|
for (const msg of logs.messages) {
|
||||||
|
html += components.chatMessage(msg.content, msg.agent);
|
||||||
|
}
|
||||||
|
chatMessages.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-apply syntax highlighting to any new code blocks
|
||||||
|
detailView.querySelectorAll('pre code:not(.hljs)').forEach((block) => {
|
||||||
|
hljs.highlightElement(block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ body {
|
|||||||
background-color: var(--bg-secondary);
|
background-color: var(--bg-secondary);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
|
font-size: 75%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
|
|||||||
@@ -992,6 +992,12 @@ impl<W: UiWriter> Agent<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_configured_context_length(config: &Config, providers: &ProviderRegistry) -> Result<u32> {
|
fn get_configured_context_length(config: &Config, providers: &ProviderRegistry) -> Result<u32> {
|
||||||
|
// First, check if there's a global max_context_length override in agent config
|
||||||
|
if let Some(max_context_length) = config.agent.max_context_length {
|
||||||
|
debug!("Using configured agent.max_context_length: {}", max_context_length);
|
||||||
|
return Ok(max_context_length);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the configured max_tokens for the current provider
|
// Get the configured max_tokens for the current provider
|
||||||
fn get_provider_max_tokens(config: &Config, provider_name: &str) -> Option<u32> {
|
fn get_provider_max_tokens(config: &Config, provider_name: &str) -> Option<u32> {
|
||||||
match provider_name {
|
match provider_name {
|
||||||
@@ -1008,12 +1014,6 @@ impl<W: UiWriter> Agent<W> {
|
|||||||
let provider_name = provider.name();
|
let provider_name = provider.name();
|
||||||
let model_name = provider.model();
|
let model_name = provider.model();
|
||||||
|
|
||||||
// Check if there's a configured context length override first
|
|
||||||
if let Some(max_tokens) = get_provider_max_tokens(config, provider_name) {
|
|
||||||
debug!("Using configured max_tokens for {}: {}", provider_name, max_tokens);
|
|
||||||
return Ok(max_tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use provider-specific context length if available, otherwise fall back to agent config
|
// Use provider-specific context length if available, otherwise fall back to agent config
|
||||||
let context_length = match provider_name {
|
let context_length = match provider_name {
|
||||||
"embedded" => {
|
"embedded" => {
|
||||||
|
|||||||
Reference in New Issue
Block a user