fix refresh and max_tokens bug

This commit is contained in:
Dhanji Prasanna
2025-11-07 09:50:43 +11:00
parent cb43fcdecf
commit 1f12ff6ca0
5 changed files with 200 additions and 24 deletions

View File

@@ -10,12 +10,18 @@ default_provider = "databricks"
host = "https://your-workspace.cloud.databricks.com"
# token = "your-databricks-token" # Optional - will use OAuth if not provided
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
use_oauth = true
[agent]
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
timeout_seconds = 60
# Retry configuration for recoverable errors (timeouts, rate limits, etc.)

View File

@@ -62,6 +62,7 @@ pub struct EmbeddedConfig {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
pub max_context_length: Option<u32>,
pub fallback_default_max_tokens: usize,
pub enable_streaming: bool,
pub timeout_seconds: u64,
@@ -135,6 +136,7 @@ impl Default for Config {
player: None, // Will use default_provider if not specified
},
agent: AgentConfig {
max_context_length: None,
fallback_default_max_tokens: 8192,
enable_streaming: true,
timeout_seconds: 60,
@@ -253,6 +255,7 @@ impl Config {
player: None, // Will use default_provider if not specified
},
agent: AgentConfig {
max_context_length: None,
fallback_default_max_tokens: 8192,
enable_streaming: true,
timeout_seconds: 60,

View File

@@ -97,6 +97,15 @@ const router = {
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) {
console.log('[Router] No instances, showing empty state');
container.innerHTML = components.emptyState(
@@ -104,15 +113,51 @@ const router = {
);
} else {
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)');
container.innerHTML = html;
// Build a map of existing panels for efficient lookup
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');
}
@@ -137,7 +182,14 @@ const router = {
console.log('[Router] renderDetail called for', id);
this.currentInstanceId = id;
container.innerHTML = components.spinner('Loading instance details...');
// 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...');
}
try {
const instance = await api.getInstance(id);
@@ -149,9 +201,24 @@ const router = {
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
let html = `
<div class="detail-view">
<div class="detail-view" data-instance-id="${id}">
<div class="detail-header">
<button class="btn btn-secondary" onclick="window.router.navigate('/')">&larr; Back</button>
<h2>${instance.workspace}</h2>
@@ -159,19 +226,19 @@ const router = {
</div>
<div class="detail-stats">
<div class="stat-card">
<div class="stat-card" data-stat="tokens">
<div class="stat-label">Tokens</div>
<div class="stat-value">${(instance.stats?.total_tokens || 0).toLocaleString()}</div>
</div>
<div class="stat-card">
<div class="stat-card" data-stat="tool_calls">
<div class="stat-label">Tool Calls</div>
<div class="stat-value">${instance.stats?.tool_calls || 0}</div>
</div>
<div class="stat-card">
<div class="stat-card" data-stat="errors">
<div class="stat-label">Errors</div>
<div class="stat-value">${instance.stats?.errors || 0}</div>
</div>
<div class="stat-card">
<div class="stat-card" data-stat="duration">
<div class="stat-label">Duration</div>
<div class="stat-value">${Math.round((instance.stats?.duration_secs || 0) / 60)}m</div>
</div>
@@ -179,17 +246,17 @@ const router = {
<div class="detail-section">
<h3>Git Status</h3>
${components.gitStatus(instance.git_status)}
<div class="git-status-container">${components.gitStatus(instance.git_status)}</div>
</div>
<div class="detail-section">
<h3>Project Files</h3>
${components.projectFiles(instance.project_files)}
<div class="project-files-container">${components.projectFiles(instance.project_files)}</div>
</div>
<div class="detail-content">
<h3>Tool Calls</h3>
<div class="tool-calls-section">
<div class="tool-calls-section" data-section="tool-calls">
`;
// Render tool calls
@@ -241,6 +308,105 @@ const router = {
console.error('[Router] Error in renderDetail:', error);
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);
}
}
};

View File

@@ -39,6 +39,7 @@ body {
background-color: var(--bg-secondary);
color: var(--text-primary);
line-height: 1.6;
font-size: 75%;
}
/* Header */

View File

@@ -992,6 +992,12 @@ impl<W: UiWriter> Agent<W> {
}
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
fn get_provider_max_tokens(config: &Config, provider_name: &str) -> Option<u32> {
match provider_name {
@@ -1008,12 +1014,6 @@ impl<W: UiWriter> Agent<W> {
let provider_name = provider.name();
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
let context_length = match provider_name {
"embedded" => {