From 66e25b4cc9dcabaaf2ea1377418bb06454e88a07 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Tue, 11 Nov 2025 22:10:56 -0500 Subject: [PATCH] refactor(glmt): remove deprecated environment variables Remove references to deprecated CCS_GLMT_FORCE_ENGLISH, CCS_GLMT_THINKING_BUDGET, and CCS_GLMT_STREAMING environment variables that were removed in v3.4.3. Changes: - README.md: Update GLMT environment variable documentation to reflect current intelligent control system - tests/unit/glmt/locale-enforcer.test.js: Remove outdated test scenario for removed environment variable - docs/glmt-controls.md: Rewrite documentation to describe new automatic control mechanisms The GLMT component now uses intelligent, automatic controls instead of manual environment variable configuration. --- README.md | 31 +- docs/glmt-controls.md | 447 +++++++++++------------- tests/unit/glmt/locale-enforcer.test.js | 37 +- 3 files changed, 220 insertions(+), 295 deletions(-) diff --git a/README.md b/README.md index 8741a111..02fea955 100644 --- a/README.md +++ b/README.md @@ -244,8 +244,7 @@ Commands and skills symlinked from `~/.ccs/shared/` - no duplication across prof **GLMT now supports real-time streaming** with incremental reasoning content delivery. - **Default**: Streaming enabled (TTFB <500ms) -- **Disable**: Set `CCS_GLMT_STREAMING=disabled` for buffered mode -- **Force**: Set `CCS_GLMT_STREAMING=force` to override client preferences +- **Auto-fallback**: Switches to buffered mode if streaming encounters errors - **Thinking parameter**: Claude CLI `thinking` parameter support - Respects `thinking.type` and `budget_tokens` - Precedence: CLI parameter > message tags > default @@ -262,22 +261,24 @@ Commands and skills symlinked from `~/.ccs/shared/` - no duplication across prof 6. Converts OpenAI `tool_calls` → Anthropic tool_use blocks 7. Thinking and tool calls appear in Claude Code UI in real-time -### Control Tags +### Control Tags & Keywords +**Control Tags**: - `` - Enable/disable reasoning blocks (default: On) - `` - Control reasoning depth (deprecated - Z.AI only supports binary thinking) +**Thinking Keywords** (automatic activation): +- `think` - Enable reasoning (low effort) +- `think hard` - Enable reasoning (medium effort) +- `think harder` - Enable reasoning (high effort) +- `ultrathink` - Maximum reasoning depth (max effort) + ### Environment Variables -**GLMT-specific**: -- `CCS_GLMT_FORCE_ENGLISH=true` - Force English output (default: true) -- `CCS_GLMT_THINKING_BUDGET=8192` - Control thinking on/off based on task type - - 0 or "unlimited": Always enable thinking - - 1-2048: Disable thinking (fast execution) - - 2049-8192: Enable for reasoning tasks only (default) - - >8192: Always enable thinking -- `CCS_GLMT_STREAMING=disabled` - Force buffered mode -- `CCS_GLMT_STREAMING=force` - Force streaming (override client) +**GLMT features**: +- Automatic English output enforcement +- Intelligent thinking mode activation based on task complexity +- Real-time streaming with automatic fallback to buffered mode **General**: - `CCS_DEBUG_LOG=1` - Enable debug file logging @@ -319,10 +320,10 @@ ccs glmt --verbose "your prompt" # Logs: ~/.ccs/logs/ ``` -**Check streaming mode**: +**GLMT debugging**: ```bash -# Disable streaming for debugging -CCS_GLMT_STREAMING=disabled ccs glmt "test" +# Verbose logging shows streaming status and reasoning details +ccs glmt --verbose "test" ``` **Check reasoning content**: diff --git a/docs/glmt-controls.md b/docs/glmt-controls.md index 5e18b4c8..7254567d 100644 --- a/docs/glmt-controls.md +++ b/docs/glmt-controls.md @@ -12,123 +12,98 @@ GLMT (GLM with Thinking) exhibited three issues: ## Solution Overview -Four control mechanisms: +Three control mechanisms: -1. **Locale enforcer** - Force English output -2. **Budget calculator** - Thinking on/off based on task type -3. **Task classifier** - Reasoning vs execution tasks -4. **Loop detection** - Break planning loops +1. **Locale enforcer** - Force English output (automatic) +2. **Task classifier** - Detect reasoning vs execution tasks +3. **Loop detection** - Break planning loops automatically ## Control Mechanisms -### 1. Locale Enforcer (`bin/locale-enforcer.js`) +### 1. Locale Enforcer (`bin/glmt/locale-enforcer.js`) **Purpose**: Prevent non-English output **Implementation**: -- Injects "MUST respond in English" into system prompts -- Default: enabled (`CCS_GLMT_FORCE_ENGLISH=true`) -- Disable: `export CCS_GLMT_FORCE_ENGLISH=false` +- Always injects "CRITICAL: You MUST respond in English only" into system prompts +- No configuration required - always enabled for consistency +- Handles both string and array content formats + +**Strategy**: +1. If system prompt exists: Prepend instruction +2. If no system prompt: Prepend to first user message +3. Preserve message structure (string vs array content) **Code**: ```javascript -function enforceLocale(request) { - if (process.env.CCS_GLMT_FORCE_ENGLISH === 'false') return request; +class LocaleEnforcer { + constructor(options = {}) { + this.instruction = "CRITICAL: You MUST respond in English only, regardless of the input language or context. This is a strict requirement."; + } - // Inject language enforcement - request.system = (request.system || '') + '\n\nMUST respond in English'; - return request; + injectInstruction(messages) { + // Clone messages to avoid mutation + const modifiedMessages = JSON.parse(JSON.stringify(messages)); + + // Strategy 1: Inject into system prompt (preferred) + const systemIndex = modifiedMessages.findIndex(m => m.role === 'system'); + if (systemIndex >= 0) { + const systemMsg = modifiedMessages[systemIndex]; + // Prepend instruction to system message content + return modifiedMessages; + } + + // Strategy 2: Prepend to first user message + const userIndex = modifiedMessages.findIndex(m => m.role === 'user'); + if (userIndex >= 0) { + const userMsg = modifiedMessages[userIndex]; + // Prepend instruction to user message content + return modifiedMessages; + } + + return modifiedMessages; + } } ``` **Files**: 85 lines -### 2. Budget Calculator (`bin/budget-calculator.js`) +### 2. Task Classifier (`bin/glmt/glmt-transformer.js`) -**Purpose**: Control thinking on/off based on task type + budget +**Purpose**: Classify tasks as reasoning vs execution for intelligent thinking activation **Implementation**: -- Reads `CCS_GLMT_THINKING_BUDGET` (default: 8192) -- Binary thinking control (Z.AI constraint: only true/false, NOT effort levels) -- Budget ranges: - - 0 or "unlimited": Always enable thinking - - 1-2048: Disable thinking (fast execution) - - 2049-8192: Enable for reasoning tasks only - - >8192: Always enable thinking +- Keyword-based classification in natural language prompts +- Automatic detection without user configuration +- Supports reasoning keywords and execution keywords -**Code**: -```javascript -function calculateBudget(taskType) { - const budget = process.env.CCS_GLMT_THINKING_BUDGET || '8192'; +**Reasoning Keywords**: +- `think`, `analyze`, `design`, `plan`, `debug`, `optimize`, `review`, `explain` +- `think hard`, `think harder`, `ultrathink` (increasing intensity levels) - if (budget === '0' || budget === 'unlimited') { - return { type: 'enabled' }; - } +**Execution Keywords**: +- `list`, `show`, `create`, `update`, `delete`, `run`, `execute`, `fix`, `implement` - const numBudget = parseInt(budget); - - if (numBudget <= 2048) { - return { type: 'disabled' }; // Fast execution - } - - if (numBudget <= 8192) { - // Enable only for reasoning tasks - return taskType === 'reasoning' - ? { type: 'enabled' } - : { type: 'disabled' }; - } - - return { type: 'enabled' }; // Always enable -} -``` - -**Files**: 109 lines - -**API Constraint**: Z.AI only supports binary thinking (true/false), NOT effort levels (low/medium/high) - -### 3. Task Classifier (`bin/task-classifier.js`) - -**Purpose**: Classify tasks as reasoning vs execution - -**Implementation**: -- Keyword-based classification -- Reasoning keywords: solve, analyze, design, plan, debug, optimize, review, explain -- Execution keywords: list, show, create, update, delete, run, execute - -**Code**: -```javascript -function classifyTask(prompt) { - const reasoningKeywords = ['solve', 'analyze', 'design', 'plan', 'debug', 'optimize', 'review', 'explain']; - const executionKeywords = ['list', 'show', 'create', 'update', 'delete', 'run', 'execute']; - - const lowerPrompt = prompt.toLowerCase(); - - const hasReasoning = reasoningKeywords.some(kw => lowerPrompt.includes(kw)); - const hasExecution = executionKeywords.some(kw => lowerPrompt.includes(kw)); - - if (hasReasoning && !hasExecution) return 'reasoning'; - if (hasExecution && !hasReasoning) return 'execution'; - - return 'mixed'; // Default to reasoning for mixed tasks -} -``` - -**Files**: 146 lines +**Priority System**: +- `ultrathink` > `think harder` > `think hard` > `think` > default +- Higher priority keywords override lower ones +- Mixed tasks default to enabled thinking **Examples**: -- "solve algorithm problem" → reasoning → thinking enabled (budget ≤8192) -- "list files in directory" → execution → thinking disabled (budget ≤8192) -- "debug authentication issue" → reasoning → thinking enabled -- "create REST API endpoint" → execution → thinking disabled +- `"think about the architecture"` → reasoning → thinking enabled +- `"list files in directory"` → execution → thinking disabled +- `"debug authentication issue"` → reasoning → thinking enabled +- `"fix the bug"` → execution → thinking disabled +- `"ultrathink this complex problem"` → maximum reasoning → thinking enabled -### 4. Loop Detection (`bin/delta-accumulator.js`) +### 3. Loop Detection (`bin/glmt/delta-accumulator.js`) **Purpose**: Break unbounded planning loops **Implementation**: - Tracks consecutive thinking blocks without tool calls -- Triggers after 3 consecutive thinking blocks -- Injects system message to force action +- Triggers after 3 consecutive thinking blocks (configurable) +- Injects system message to force execution mode **Code**: ```javascript @@ -147,223 +122,207 @@ class DeltaAccumulator { } } - if (event.type === 'tool_use') { - // Reset counter on tool calls + if (event.type === 'tool_call' || event.type === 'tool_result') { + // Reset counter on tool activity this.consecutiveThinkingBlocks = 0; } } - - injectLoopBreaker() { - return { - type: 'message', - role: 'system', - content: 'Planning loop detected. Execute action now.' - }; - } } ``` -**Files**: 156 lines (enhanced) +**Loop Breaker Message**: +``` +STOP thinking and start executing. You've been planning too long without taking action. +Please provide concrete solutions or use available tools to complete the task. +``` -**Trigger condition**: 3 consecutive thinking blocks with no tool calls +**Files**: 146 lines -## Integration +## Control Tags & Keywords -All controls integrated into `bin/glmt-transformer.js`: +### Control Tags (Manual Control) +- `` - Enable/disable reasoning blocks (default: On) +- `` - Deprecated - Z.AI only supports binary thinking + +### Thinking Keywords (Automatic Activation) +- `think` - Enable reasoning (low effort) +- `think hard` - Enable reasoning (medium effort) +- `think harder` - Enable reasoning (high effort) +- `ultrathink` - Maximum reasoning depth (max effort) + +**Usage Examples**: +```bash +ccs glmt "think about the microservices architecture" +ccs glmt "ultrathink this complex algorithm optimization" +ccs glmt "implement the user authentication feature" +ccs glmt "debug the memory leak issue" +``` + +## Integration Flow ```javascript -// 1. Locale enforcement -const localeEnforcer = require('./locale-enforcer'); -request = localeEnforcer.enforce(request); +// 1. Locale enforcement (always applied) +const localeEnforcer = new LocaleEnforcer(); +const messagesWithLocale = localeEnforcer.injectInstruction(request.messages); -// 2. Task classification + budget control -const taskClassifier = require('./task-classifier'); -const budgetCalculator = require('./budget-calculator'); - -const taskType = taskClassifier.classify(request.messages[0].content); -const thinkingConfig = budgetCalculator.calculate(taskType); +// 2. Task classification (automatic) +const taskClassifier = new TaskClassifier(); // Built into transformer +const thinkingConfig = taskClassifier.classifyTask(prompt); +// 3. Apply thinking configuration request.thinking = thinkingConfig; -// 3. Loop detection (during streaming) +// 4. Loop detection (during streaming) const deltaAccumulator = new DeltaAccumulator(); deltaAccumulator.trackThinkingLoop(event); ``` ## Environment Variables -### CCS_GLMT_FORCE_ENGLISH +### General Environment Variables -**Default**: `true` +**CCS_DEBUG=1** +- Enable debug logging (file logging to ~/.ccs/logs/ + enhanced console diagnostics) +- Shows reasoning deltas, block creation, and loop detection activity -**Values**: -- `true` - Force English output (inject language enforcement) -- `false` - Allow model default language - -**Usage**: -```bash -# Enable (default) -export CCS_GLMT_FORCE_ENGLISH=true - -# Disable -export CCS_GLMT_FORCE_ENGLISH=false -``` - -### CCS_GLMT_THINKING_BUDGET - -**Default**: `8192` - -**Values**: -- `0` or `unlimited` - Always enable thinking -- `1-2048` - Disable thinking (fast execution) -- `2049-8192` - Enable for reasoning tasks only (default) -- `>8192` - Always enable thinking - -**Usage**: -```bash -# Default (reasoning tasks only) -export CCS_GLMT_THINKING_BUDGET=8192 - -# Always enable thinking -export CCS_GLMT_THINKING_BUDGET=0 -export CCS_GLMT_THINKING_BUDGET=unlimited - -# Disable thinking (fast execution) -export CCS_GLMT_THINKING_BUDGET=1024 - -# Always enable thinking (high budget) -export CCS_GLMT_THINKING_BUDGET=16384 -``` +**CCS_CLAUDE_PATH=/path/to/claude** +- Custom Claude CLI path for non-standard installations ## Testing -**Test coverage**: 110 tests passing +GLMT includes comprehensive test coverage: -**Test files**: -- `tests/glmt-transformer.test.js` - All control mechanisms covered - -**Run tests**: ```bash -npm test +# Locale enforcer tests +npm test -- tests/unit/glmt/locale-enforcer.test.js + +# GLMT transformer tests +npm test -- tests/unit/glmt/glmt-transformer.test.js + +# Integration tests +npm test -- tests/integration/glmt/ ``` +**Test Coverage**: 35+ tests covering: +- Locale enforcement (3 scenarios) +- Task classification and thinking activation +- Loop detection and breaker injection +- Streaming transformation and delta accumulation +- Tool calling support and bidirectional transformation + ## Troubleshooting -### Chinese Output Despite CCS_GLMT_FORCE_ENGLISH=true +### Chinese Output Despite Locale Enforcement -1. Check environment variable: +**Expected**: Should never happen with current implementation + +**If it occurs**: +1. Check for malformed messages in debug logs +2. Verify locale enforcer is being called in proxy flow +3. Check system message content in transformation logs + +**Debug**: ```bash -echo $CCS_GLMT_FORCE_ENGLISH # Should be "true" +export CCS_DEBUG=1 +ccs glmt "test prompt" +# Check logs: ~/.ccs/logs/*request-openai.json ``` -2. Verify locale enforcer enabled: +### Excessive Planning Loops + +**Symptoms**: Multiple consecutive thinking blocks without tool calls + +**Expected behavior**: Loop detector should trigger after 3 blocks + +**If loops persist**: +1. Check loop detector logs: `export CCS_DEBUG=1` +2. Verify consecutive thinking counter reset on tool calls +3. Check loop breaker message injection + +**Manual intervention**: ```bash -CCS_DEBUG_LOG=1 ccs glmt "test" -cat ~/.ccs/logs/*request-openai.json | jq '.system' | grep "MUST respond in English" +# Use specific execution keywords to bypass thinking +ccs glmt "implement the solution now" +ccs glmt "fix the bug immediately" +ccs glmt "execute the code" ``` -3. If absent: locale enforcer not applied - check implementation +### No Thinking Blocks on Complex Tasks -### Thinking Blocks Not Appearing +**Symptoms**: Straight to execution without reasoning -1. Check budget setting: -```bash -echo $CCS_GLMT_THINKING_BUDGET # Default: 8192 -``` +**Cause**: Task classifier may not recognize reasoning keywords -2. Check task classification: -```bash -# "list files" → execution → thinking disabled (budget=8192) -# "solve problem" → reasoning → thinking enabled (budget=8192) -``` - -3. Override budget: -```bash -# Always enable thinking -export CCS_GLMT_THINKING_BUDGET=0 -ccs glmt "your prompt" -``` - -### Unbounded Planning Loops - -1. Loop detection triggers after 3 consecutive thinking blocks -2. Check logs: -```bash -CCS_DEBUG_LOG=1 ccs glmt "test" -cat ~/.ccs/logs/*debug.log | grep "Planning loop detected" -``` - -3. If loops persist: - - Lower budget: `export CCS_GLMT_THINKING_BUDGET=1024` - - Disable thinking: Force execution mode +**Solutions**: +1. Use explicit thinking keywords: + ```bash + ccs glmt "think about this problem" + ccs glmt "ultrathink the architecture" + ``` +2. Use control tags: + ```bash + ccs glmt " analyze this complex issue" + ``` +3. Check if task classification working in debug logs ### Token Waste on Simple Tasks -1. Check default budget (8192 = reasoning tasks only) -2. Lower budget for stricter control: -```bash -export CCS_GLMT_THINKING_BUDGET=2048 -``` +**Expected behavior**: Task classifier should disable thinking for execution tasks -3. Verify task classification: -```bash -# Execution tasks should disable thinking at budget=8192 -ccs glmt "list files" # Should be fast (no thinking) -ccs glmt "solve algorithm" # Should use thinking -``` +**If thinking still enabled**: +1. Check for mixed keywords in prompt (both reasoning and execution) +2. Use explicit execution keywords: `fix`, `implement`, `execute`, `create` +3. Verify task classification in debug logs -## Performance Impact +## Architecture Notes -**Token savings**: -- Execution tasks: ~50-80% token reduction (thinking disabled) -- Reasoning tasks: No change (thinking enabled as needed) +### Z.AI API Constraints -**Latency impact**: -- Execution tasks: ~30-50% faster (no thinking overhead) -- Reasoning tasks: No change +- **Binary thinking only**: Z.AI supports `thinking_enabled: true/false`, not effort levels +- **Reasoning content**: Delivered via `reasoning_content` field in API responses +- **Tool calling**: Full OpenAI-compatible function calling supported +- **Streaming**: Real-time delivery of reasoning content and tool calls -**Loop detection**: -- Breaks infinite loops after 3 blocks -- Prevents exponential token waste +### Backward Compatibility -## Implementation Files +- **Control tags**: `` still work alongside keywords +- **Claude CLI thinking parameter**: Respects `thinking.type` and `budget_tokens` +- **Precedence**: CLI parameter > message tags > keywords > default -| File | Lines | Purpose | -|------|-------|---------| -| `bin/locale-enforcer.js` | 85 | Force English output | -| `bin/budget-calculator.js` | 109 | Thinking on/off control | -| `bin/task-classifier.js` | 146 | Task classification | -| `bin/delta-accumulator.js` | 156 | Loop detection (enhanced) | -| `bin/glmt-transformer.js` | 685 | Integration + transformation | +### Performance -**Total**: ~1200 lines (control mechanisms + transformation) +- **TTFB**: <500ms for streaming mode +- **Auto-fallback**: Switches to buffered mode if streaming errors +- **Loop prevention**: Eliminates token waste from unbounded planning +- **Intelligent activation**: Thinking only when beneficial -## API Constraints +## Security Limits -**Z.AI limitations**: -- Only supports binary thinking (true/false) -- Does NOT support effort levels (low/medium/high) -- `` tags deprecated -- Use `CCS_GLMT_THINKING_BUDGET` for control instead +**DoS protection** (built into proxy): +- SSE buffer: 1MB max per event +- Content buffer: 10MB max per block (thinking/text) +- Content blocks: 100 max per message +- Request timeout: 120s (both streaming and buffered) -**Backward compatibility**: -- Control tags still work (``) -- Effort tags ignored (mapped to binary thinking) +**Loop protection**: +- Maximum 3 consecutive thinking blocks +- Automatic loop breaker injection +- Prevents unlimited token consumption -## Future Enhancements +## Migration Notes -Potential improvements: +### From Environment Variables (v3.5+) -1. **LLM-based task classification** - More accurate than keywords -2. **Adaptive budget** - Learn from task history -3. **Per-task budget overrides** - Fine-grained control -4. **Loop detection thresholds** - Configurable trigger count -5. **Multi-language support** - Beyond English enforcement +The following environment variables have been **removed**: +- ~~`CCS_GLMT_FORCE_ENGLISH`~~ → Now always enabled +- ~~`CCS_GLMT_THINKING_BUDGET`~~ → Replaced by intelligent task classification +- ~~`CCS_GLMT_STREAMING`~~ → Automatic streaming with fallback -Not implemented (YAGNI principle). +**No action required** - GLMT automatically handles all these cases intelligently. -## Related Documentation +### New Features (v3.5+) -- [CLAUDE.md](../CLAUDE.md) - Architecture overview -- [README.md](../README.md) - User guide -- [system-architecture.md](./system-architecture.md) - System design +- **Thinking keywords**: Natural language control (`think`, `think hard`, etc.) +- **Loop detection**: Automatic prevention of planning loops +- **Enhanced streaming**: Better error handling and auto-fallback +- **Tool support**: Full MCP tools and function calling compatibility \ No newline at end of file diff --git a/tests/unit/glmt/locale-enforcer.test.js b/tests/unit/glmt/locale-enforcer.test.js index 8948d7a9..04461752 100644 --- a/tests/unit/glmt/locale-enforcer.test.js +++ b/tests/unit/glmt/locale-enforcer.test.js @@ -4,11 +4,10 @@ /** * LocaleEnforcer Unit Tests * - * Tests 4 scenarios: + * Tests 3 scenarios: * 1. English prompt → English output (verify instruction injected) * 2. Chinese prompt → English output (verify instruction injected) * 3. Mixed prompt → English output (verify instruction injected) - * 4. Opt-out test: CCS_GLMT_FORCE_ENGLISH=false (allow multilingual) */ const assert = require('assert'); @@ -136,40 +135,6 @@ describe('LocaleEnforcer', () => { }); }); - describe('Scenario 4: Opt-out (CCS_GLMT_FORCE_ENGLISH=false)', () => { - it('should not inject instruction when forceEnglish is disabled', () => { - const enforcer = new LocaleEnforcer({ forceEnglish: false }); - const messages = [ - { role: 'system', content: '你是一个编程助手' }, - { role: 'user', content: '实现用户认证' } - ]; - - const result = enforcer.injectInstruction(messages); - - assert.strictEqual(result.length, 2); - assert.strictEqual(result[0].content, '你是一个编程助手'); - assert.strictEqual(result[1].content, '实现用户认证'); - assert.ok(!result[0].content.includes('CRITICAL: You MUST respond in English only')); - }); - - it('should pass through messages unchanged when disabled', () => { - const enforcer = new LocaleEnforcer({ forceEnglish: false }); - const originalMessages = [ - { - role: 'user', - content: [ - { type: 'text', text: 'Debug the code' }, - { type: 'text', text: '修复这个错误' } - ] - } - ]; - - const result = enforcer.injectInstruction(originalMessages); - - assert.deepStrictEqual(result, originalMessages); - }); - }); - describe('Edge cases', () => { it('should handle empty messages array', () => { const enforcer = new LocaleEnforcer({ forceEnglish: true });