mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-28 07:16:53 +05:00
Compare commits
4 Commits
ipuaro-v0.
...
ipuaro-v0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c94335729 | ||
|
|
c34d57c231 | ||
|
|
60052c0db9 | ||
|
|
fa647c41aa |
@@ -5,6 +5,154 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.22.5] - 2025-12-02 - Commands Configuration
|
||||
|
||||
### Added
|
||||
|
||||
- **CommandsConfigSchema (0.22.5)**
|
||||
- New configuration schema for command settings in `src/shared/constants/config.ts`
|
||||
- `timeout: number | null` (default: null) - global timeout for shell commands in milliseconds
|
||||
- Integrated into main ConfigSchema with `.default({})`
|
||||
- Exported `CommandsConfig` type from config module
|
||||
|
||||
### Changed
|
||||
|
||||
- **RunCommandTool**
|
||||
- Added optional `config?: CommandsConfig` parameter to constructor
|
||||
- Timeout priority: `params.timeout` → `config.timeout` → `DEFAULT_TIMEOUT (30000)`
|
||||
- Updated parameter description to reflect configuration support
|
||||
- Config-based timeout enables global command timeout without per-call specification
|
||||
|
||||
### Technical Details
|
||||
|
||||
- Total tests: 1679 passed (was 1657, +22 new tests)
|
||||
- New test file: `commands-config.test.ts` with 19 tests
|
||||
- Default values validation (timeout: null)
|
||||
- `timeout` nullable positive integer validation (including edge cases: zero, negative, float rejection)
|
||||
- Partial and full config merging tests
|
||||
- Updated RunCommandTool tests: 3 new tests for configuration integration
|
||||
- Config timeout behavior
|
||||
- Null config timeout fallback to default
|
||||
- Param timeout priority over config timeout
|
||||
- Coverage: 97.64% lines, 91.36% branches, 98.77% functions, 97.64% statements
|
||||
- 0 ESLint errors, 5 warnings (acceptable TUI component warnings)
|
||||
- Build successful with no TypeScript errors
|
||||
|
||||
### Notes
|
||||
|
||||
This release completes the v0.22.0 Extended Configuration milestone. All items for v0.22.0 are now complete:
|
||||
- ✅ 0.22.1 - Display Configuration
|
||||
- ✅ 0.22.2 - Session Configuration
|
||||
- ✅ 0.22.3 - Context Configuration
|
||||
- ✅ 0.22.4 - Autocomplete Configuration
|
||||
- ✅ 0.22.5 - Commands Configuration
|
||||
|
||||
---
|
||||
|
||||
## [0.22.4] - 2025-12-02 - Autocomplete Configuration
|
||||
|
||||
### Added
|
||||
|
||||
- **AutocompleteConfigSchema (0.22.4)**
|
||||
- New configuration schema for autocomplete settings in `src/shared/constants/config.ts`
|
||||
- `enabled: boolean` (default: true) - toggle autocomplete feature
|
||||
- `source: "redis-index" | "filesystem" | "both"` (default: "redis-index") - autocomplete source
|
||||
- `maxSuggestions: number` (default: 10) - maximum number of suggestions to display
|
||||
- Integrated into main ConfigSchema with `.default({})`
|
||||
- Exported `AutocompleteConfig` type from config module
|
||||
|
||||
### Changed
|
||||
|
||||
- **useAutocomplete Hook**
|
||||
- Added optional `config?: AutocompleteConfig` parameter to `UseAutocompleteOptions`
|
||||
- Config priority: `config` → `props` → `defaults`
|
||||
- Reads `enabled` and `maxSuggestions` from config if provided
|
||||
- Falls back to prop values, then to defaults
|
||||
- Internal variables renamed: `enabled` → `isEnabled`, `maxSuggestions` → `maxSuggestionsCount`
|
||||
|
||||
- **Chat Component**
|
||||
- Fixed ESLint error: removed unused `roleColor` variable in `ToolMessage` component
|
||||
- Removed unused `theme` parameter from `ToolMessage` function signature
|
||||
|
||||
### Technical Details
|
||||
|
||||
- Total tests: 1657 passed (was 1630, +27 new tests)
|
||||
- New test file: `autocomplete-config.test.ts` with 27 tests
|
||||
- Default values validation (enabled, source, maxSuggestions)
|
||||
- `enabled` boolean validation
|
||||
- `source` enum validation ("redis-index", "filesystem", "both")
|
||||
- `maxSuggestions` positive integer validation (including edge cases: zero, negative, float rejection)
|
||||
- Partial and full config merging tests
|
||||
- Coverage: 97.59% lines, 91.23% branches, 98.77% functions, 97.59% statements
|
||||
- 0 ESLint errors, 5 warnings (acceptable TUI component warnings)
|
||||
- Build successful with no TypeScript errors
|
||||
|
||||
### Notes
|
||||
|
||||
This release completes the fourth item (0.22.4) of the v0.22.0 Extended Configuration milestone. Remaining item for v0.22.0:
|
||||
- 0.22.5 - Commands Configuration
|
||||
|
||||
---
|
||||
|
||||
## [0.22.3] - 2025-12-02 - Context Configuration
|
||||
|
||||
### Added
|
||||
|
||||
- **ContextConfigSchema (0.22.3)**
|
||||
- New configuration schema for context management in `src/shared/constants/config.ts`
|
||||
- `systemPromptTokens: number` (default: 2000) - token budget for system prompt
|
||||
- `maxContextUsage: number` (default: 0.8) - maximum context window usage ratio (0-1)
|
||||
- `autoCompressAt: number` (default: 0.8) - threshold for automatic context compression (0-1)
|
||||
- `compressionMethod: "llm-summary" | "truncate"` (default: "llm-summary") - compression strategy
|
||||
- Integrated into main ConfigSchema with `.default({})`
|
||||
- Exported `ContextConfig` type from config module
|
||||
|
||||
### Changed
|
||||
|
||||
- **ContextManager**
|
||||
- Added optional `config?: ContextConfig` parameter to constructor
|
||||
- Added private `compressionThreshold: number` field (read from config or default)
|
||||
- Added private `compressionMethod: "llm-summary" | "truncate"` field (read from config or default)
|
||||
- Updated `needsCompression()` to use configurable `compressionThreshold` instead of hardcoded constant
|
||||
- Enables dynamic compression threshold configuration per session/deployment
|
||||
|
||||
- **HandleMessage Use Case**
|
||||
- Added optional `contextConfig?: ContextConfig` parameter to constructor
|
||||
- Added `contextConfig?: ContextConfig` to `HandleMessageOptions`
|
||||
- Passes context config to ContextManager during initialization
|
||||
- Context management behavior now fully configurable
|
||||
|
||||
- **useSession Hook**
|
||||
- Passes `deps.config?.context` to HandleMessage constructor
|
||||
- Passes `contextConfig: deps.config?.context` to HandleMessage options
|
||||
- Context configuration flows from config through to ContextManager
|
||||
|
||||
### Technical Details
|
||||
|
||||
- Total tests: 1630 passed (was 1590, +40 new tests)
|
||||
- New test file: `context-config.test.ts` with 32 tests
|
||||
- Default values validation (systemPromptTokens, maxContextUsage, autoCompressAt, compressionMethod)
|
||||
- `systemPromptTokens` positive integer validation (including edge cases: zero, negative, float rejection)
|
||||
- `maxContextUsage` ratio validation (0-1 range, rejects out-of-bounds)
|
||||
- `autoCompressAt` ratio validation (0-1 range, rejects out-of-bounds)
|
||||
- `compressionMethod` enum validation (llm-summary, truncate)
|
||||
- Partial and full config merging tests
|
||||
- Updated ContextManager tests: 8 new tests for configuration integration
|
||||
- Custom compression threshold behavior
|
||||
- Edge cases: autoCompressAt = 0 and autoCompressAt = 1
|
||||
- Full context config acceptance
|
||||
- Coverage: 97.63% lines, 91.34% branches, 98.77% functions, 97.63% statements
|
||||
- 0 ESLint errors, 0 warnings
|
||||
- Build successful with no TypeScript errors
|
||||
|
||||
### Notes
|
||||
|
||||
This release completes the third item (0.22.3) of the v0.22.0 Extended Configuration milestone. Remaining items for v0.22.0:
|
||||
- 0.22.4 - Autocomplete Configuration
|
||||
- 0.22.5 - Commands Configuration
|
||||
|
||||
---
|
||||
|
||||
## [0.22.2] - 2025-12-02 - Session Configuration
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1648,7 +1648,7 @@ interface DiffViewProps {
|
||||
## Version 0.22.0 - Extended Configuration ⚙️
|
||||
|
||||
**Priority:** MEDIUM
|
||||
**Status:** In Progress (2/5 complete)
|
||||
**Status:** Complete (5/5 complete) ✅
|
||||
|
||||
### 0.22.1 - Display Configuration ✅
|
||||
|
||||
@@ -1687,7 +1687,7 @@ export const SessionConfigSchema = z.object({
|
||||
- [x] Input history persistence toggle
|
||||
- [x] Unit tests (19 new tests)
|
||||
|
||||
### 0.22.3 - Context Configuration
|
||||
### 0.22.3 - Context Configuration ✅
|
||||
|
||||
```typescript
|
||||
// src/shared/constants/config.ts additions
|
||||
@@ -1700,12 +1700,12 @@ export const ContextConfigSchema = z.object({
|
||||
```
|
||||
|
||||
**Deliverables:**
|
||||
- [ ] ContextConfigSchema in config.ts
|
||||
- [ ] ContextManager reads from config
|
||||
- [ ] Configurable compression threshold
|
||||
- [ ] Unit tests
|
||||
- [x] ContextConfigSchema in config.ts
|
||||
- [x] ContextManager reads from config
|
||||
- [x] Configurable compression threshold
|
||||
- [x] Unit tests (40 new tests: 32 schema, 8 ContextManager integration)
|
||||
|
||||
### 0.22.4 - Autocomplete Configuration
|
||||
### 0.22.4 - Autocomplete Configuration ✅
|
||||
|
||||
```typescript
|
||||
// src/shared/constants/config.ts additions
|
||||
@@ -1717,11 +1717,11 @@ export const AutocompleteConfigSchema = z.object({
|
||||
```
|
||||
|
||||
**Deliverables:**
|
||||
- [ ] AutocompleteConfigSchema in config.ts
|
||||
- [ ] useAutocomplete reads from config
|
||||
- [ ] Unit tests
|
||||
- [x] AutocompleteConfigSchema in config.ts
|
||||
- [x] useAutocomplete reads from config
|
||||
- [x] Unit tests (27 tests)
|
||||
|
||||
### 0.22.5 - Commands Configuration
|
||||
### 0.22.5 - Commands Configuration ✅
|
||||
|
||||
```typescript
|
||||
// src/shared/constants/config.ts additions
|
||||
@@ -1731,13 +1731,13 @@ export const CommandsConfigSchema = z.object({
|
||||
```
|
||||
|
||||
**Deliverables:**
|
||||
- [ ] CommandsConfigSchema in config.ts
|
||||
- [ ] Timeout support for run_command tool
|
||||
- [ ] Unit tests
|
||||
- [x] CommandsConfigSchema in config.ts
|
||||
- [x] Timeout support for run_command tool
|
||||
- [x] Unit tests (19 schema tests + 3 RunCommandTool integration tests)
|
||||
|
||||
**Tests:**
|
||||
- [ ] Unit tests for all new config schemas
|
||||
- [ ] Integration tests for config loading
|
||||
- [x] Unit tests for CommandsConfigSchema (19 tests)
|
||||
- [x] Integration tests for RunCommandTool with config (3 tests)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@samiyev/ipuaro",
|
||||
"version": "0.22.2",
|
||||
"version": "0.22.4",
|
||||
"description": "Local AI agent for codebase operations with infinite context feeling",
|
||||
"author": "Fozilbek Samiyev <fozilbek.samiyev@gmail.com>",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { ContextState, Session } from "../../domain/entities/Session.js"
|
||||
import type { ILLMClient } from "../../domain/services/ILLMClient.js"
|
||||
import { type ChatMessage, createSystemMessage } from "../../domain/value-objects/ChatMessage.js"
|
||||
import { CONTEXT_COMPRESSION_THRESHOLD, CONTEXT_WINDOW_SIZE } from "../../domain/constants/index.js"
|
||||
import type { ContextConfig } from "../../shared/constants/config.js"
|
||||
|
||||
/**
|
||||
* File in context with token count.
|
||||
@@ -39,9 +40,13 @@ export class ContextManager {
|
||||
private readonly filesInContext = new Map<string, FileContext>()
|
||||
private currentTokens = 0
|
||||
private readonly contextWindowSize: number
|
||||
private readonly compressionThreshold: number
|
||||
private readonly compressionMethod: "llm-summary" | "truncate"
|
||||
|
||||
constructor(contextWindowSize: number = CONTEXT_WINDOW_SIZE) {
|
||||
constructor(contextWindowSize: number = CONTEXT_WINDOW_SIZE, config?: ContextConfig) {
|
||||
this.contextWindowSize = contextWindowSize
|
||||
this.compressionThreshold = config?.autoCompressAt ?? CONTEXT_COMPRESSION_THRESHOLD
|
||||
this.compressionMethod = config?.compressionMethod ?? "llm-summary"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +102,7 @@ export class ContextManager {
|
||||
* Check if compression is needed.
|
||||
*/
|
||||
needsCompression(): boolean {
|
||||
return this.getUsage() > CONTEXT_COMPRESSION_THRESHOLD
|
||||
return this.getUsage() > this.compressionThreshold
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,6 +70,7 @@ export interface HandleMessageOptions {
|
||||
maxToolCalls?: number
|
||||
maxHistoryMessages?: number
|
||||
saveInputHistory?: boolean
|
||||
contextConfig?: import("../../shared/constants/config.js").ContextConfig
|
||||
}
|
||||
|
||||
const DEFAULT_MAX_TOOL_CALLS = 20
|
||||
@@ -98,13 +99,14 @@ export class HandleMessage {
|
||||
llm: ILLMClient,
|
||||
tools: IToolRegistry,
|
||||
projectRoot: string,
|
||||
contextConfig?: import("../../shared/constants/config.js").ContextConfig,
|
||||
) {
|
||||
this.storage = storage
|
||||
this.sessionStorage = sessionStorage
|
||||
this.llm = llm
|
||||
this.tools = tools
|
||||
this.projectRoot = projectRoot
|
||||
this.contextManager = new ContextManager(llm.getContextWindowSize())
|
||||
this.contextManager = new ContextManager(llm.getContextWindowSize(), contextConfig)
|
||||
this.executeTool = new ExecuteTool(storage, sessionStorage, tools, projectRoot)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
createSuccessResult,
|
||||
type ToolResult,
|
||||
} from "../../../domain/value-objects/ToolResult.js"
|
||||
import type { CommandsConfig } from "../../../shared/constants/config.js"
|
||||
import { CommandSecurity } from "./CommandSecurity.js"
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
@@ -60,7 +61,7 @@ export class RunCommandTool implements ITool {
|
||||
{
|
||||
name: "timeout",
|
||||
type: "number",
|
||||
description: "Timeout in milliseconds (default: 30000)",
|
||||
description: "Timeout in milliseconds (default: from config or 30000, max: 600000)",
|
||||
required: false,
|
||||
},
|
||||
]
|
||||
@@ -69,10 +70,12 @@ export class RunCommandTool implements ITool {
|
||||
|
||||
private readonly security: CommandSecurity
|
||||
private readonly execFn: typeof execAsync
|
||||
private readonly configTimeout: number | null
|
||||
|
||||
constructor(security?: CommandSecurity, execFn?: typeof execAsync) {
|
||||
constructor(security?: CommandSecurity, execFn?: typeof execAsync, config?: CommandsConfig) {
|
||||
this.security = security ?? new CommandSecurity()
|
||||
this.execFn = execFn ?? execAsync
|
||||
this.configTimeout = config?.timeout ?? null
|
||||
}
|
||||
|
||||
validateParams(params: Record<string, unknown>): string | null {
|
||||
@@ -104,7 +107,7 @@ export class RunCommandTool implements ITool {
|
||||
const callId = `${this.name}-${String(startTime)}`
|
||||
|
||||
const command = params.command as string
|
||||
const timeout = (params.timeout as number) ?? DEFAULT_TIMEOUT
|
||||
const timeout = (params.timeout as number) ?? this.configTimeout ?? DEFAULT_TIMEOUT
|
||||
|
||||
const securityCheck = this.security.check(command)
|
||||
|
||||
|
||||
@@ -106,6 +106,32 @@ export const SessionConfigSchema = z.object({
|
||||
saveInputHistory: z.boolean().default(true),
|
||||
})
|
||||
|
||||
/**
|
||||
* Context configuration schema.
|
||||
*/
|
||||
export const ContextConfigSchema = z.object({
|
||||
systemPromptTokens: z.number().int().positive().default(2000),
|
||||
maxContextUsage: z.number().min(0).max(1).default(0.8),
|
||||
autoCompressAt: z.number().min(0).max(1).default(0.8),
|
||||
compressionMethod: z.enum(["llm-summary", "truncate"]).default("llm-summary"),
|
||||
})
|
||||
|
||||
/**
|
||||
* Autocomplete configuration schema.
|
||||
*/
|
||||
export const AutocompleteConfigSchema = z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
source: z.enum(["redis-index", "filesystem", "both"]).default("redis-index"),
|
||||
maxSuggestions: z.number().int().positive().default(10),
|
||||
})
|
||||
|
||||
/**
|
||||
* Commands configuration schema.
|
||||
*/
|
||||
export const CommandsConfigSchema = z.object({
|
||||
timeout: z.number().int().positive().nullable().default(null),
|
||||
})
|
||||
|
||||
/**
|
||||
* Full configuration schema.
|
||||
*/
|
||||
@@ -119,6 +145,9 @@ export const ConfigSchema = z.object({
|
||||
input: InputConfigSchema.default({}),
|
||||
display: DisplayConfigSchema.default({}),
|
||||
session: SessionConfigSchema.default({}),
|
||||
context: ContextConfigSchema.default({}),
|
||||
autocomplete: AutocompleteConfigSchema.default({}),
|
||||
commands: CommandsConfigSchema.default({}),
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -134,6 +163,9 @@ export type EditConfig = z.infer<typeof EditConfigSchema>
|
||||
export type InputConfig = z.infer<typeof InputConfigSchema>
|
||||
export type DisplayConfig = z.infer<typeof DisplayConfigSchema>
|
||||
export type SessionConfig = z.infer<typeof SessionConfigSchema>
|
||||
export type ContextConfig = z.infer<typeof ContextConfigSchema>
|
||||
export type AutocompleteConfig = z.infer<typeof AutocompleteConfigSchema>
|
||||
export type CommandsConfig = z.infer<typeof CommandsConfigSchema>
|
||||
|
||||
/**
|
||||
* Default configuration.
|
||||
|
||||
@@ -120,9 +120,7 @@ function AssistantMessage({
|
||||
)
|
||||
}
|
||||
|
||||
function ToolMessage({ message, theme }: MessageComponentProps): React.JSX.Element {
|
||||
const roleColor = getRoleColor("tool", theme)
|
||||
|
||||
function ToolMessage({ message }: MessageComponentProps): React.JSX.Element {
|
||||
return (
|
||||
<Box flexDirection="column" marginBottom={1} marginLeft={2}>
|
||||
{message.toolResults?.map((result) => (
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import type { IStorage } from "../../domain/services/IStorage.js"
|
||||
import type { AutocompleteConfig } from "../../shared/constants/config.js"
|
||||
import path from "node:path"
|
||||
|
||||
export interface UseAutocompleteOptions {
|
||||
@@ -12,6 +13,7 @@ export interface UseAutocompleteOptions {
|
||||
projectRoot: string
|
||||
enabled?: boolean
|
||||
maxSuggestions?: number
|
||||
config?: AutocompleteConfig
|
||||
}
|
||||
|
||||
export interface UseAutocompleteReturn {
|
||||
@@ -107,13 +109,18 @@ function getCommonPrefix(suggestions: string[]): string {
|
||||
}
|
||||
|
||||
export function useAutocomplete(options: UseAutocompleteOptions): UseAutocompleteReturn {
|
||||
const { storage, projectRoot, enabled = true, maxSuggestions = 10 } = options
|
||||
const { storage, projectRoot, enabled, maxSuggestions, config } = options
|
||||
|
||||
// Read from config if provided, otherwise use options, otherwise use defaults
|
||||
const isEnabled = config?.enabled ?? enabled ?? true
|
||||
const maxSuggestionsCount = config?.maxSuggestions ?? maxSuggestions ?? 10
|
||||
|
||||
const [filePaths, setFilePaths] = useState<string[]>([])
|
||||
const [suggestions, setSuggestions] = useState<string[]>([])
|
||||
|
||||
// Load file paths from storage
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
if (!isEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -135,11 +142,11 @@ export function useAutocomplete(options: UseAutocompleteOptions): UseAutocomplet
|
||||
loadPaths().catch(() => {
|
||||
// Ignore errors
|
||||
})
|
||||
}, [storage, projectRoot, enabled])
|
||||
}, [storage, projectRoot, isEnabled])
|
||||
|
||||
const complete = useCallback(
|
||||
(partial: string): string[] => {
|
||||
if (!enabled || !partial.trim()) {
|
||||
if (!isEnabled || !partial.trim()) {
|
||||
setSuggestions([])
|
||||
return []
|
||||
}
|
||||
@@ -154,13 +161,13 @@ export function useAutocomplete(options: UseAutocompleteOptions): UseAutocomplet
|
||||
}))
|
||||
.filter((item) => item.score > 0)
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, maxSuggestions)
|
||||
.slice(0, maxSuggestionsCount)
|
||||
.map((item) => item.path)
|
||||
|
||||
setSuggestions(scored)
|
||||
return scored
|
||||
},
|
||||
[enabled, filePaths, maxSuggestions],
|
||||
[isEnabled, filePaths, maxSuggestionsCount],
|
||||
)
|
||||
|
||||
const accept = useCallback(
|
||||
|
||||
@@ -109,6 +109,7 @@ async function initializeSession(
|
||||
deps.llm,
|
||||
deps.tools,
|
||||
deps.projectRoot,
|
||||
deps.config?.context,
|
||||
)
|
||||
if (deps.projectStructure) {
|
||||
handleMessage.setProjectStructure(deps.projectStructure)
|
||||
@@ -117,6 +118,7 @@ async function initializeSession(
|
||||
autoApply: options.autoApply,
|
||||
maxHistoryMessages: deps.config?.session.maxHistoryMessages,
|
||||
saveInputHistory: deps.config?.session.saveInputHistory,
|
||||
contextConfig: deps.config?.context,
|
||||
})
|
||||
handleMessage.setEvents(createEventHandlers(setters, options))
|
||||
refs.current.handleMessage = handleMessage
|
||||
|
||||
@@ -245,4 +245,65 @@ describe("ContextManager", () => {
|
||||
expect(state.needsCompression).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("configuration", () => {
|
||||
it("should use default compression threshold when no config provided", () => {
|
||||
const manager = new ContextManager(CONTEXT_SIZE)
|
||||
manager.addToContext("test.ts", CONTEXT_SIZE * 0.85)
|
||||
|
||||
expect(manager.needsCompression()).toBe(true)
|
||||
})
|
||||
|
||||
it("should use custom compression threshold from config", () => {
|
||||
const manager = new ContextManager(CONTEXT_SIZE, { autoCompressAt: 0.9 })
|
||||
manager.addToContext("test.ts", CONTEXT_SIZE * 0.85)
|
||||
|
||||
expect(manager.needsCompression()).toBe(false)
|
||||
})
|
||||
|
||||
it("should trigger compression at custom threshold", () => {
|
||||
const manager = new ContextManager(CONTEXT_SIZE, { autoCompressAt: 0.9 })
|
||||
manager.addToContext("test.ts", CONTEXT_SIZE * 0.95)
|
||||
|
||||
expect(manager.needsCompression()).toBe(true)
|
||||
})
|
||||
|
||||
it("should accept compression method in config", () => {
|
||||
const manager = new ContextManager(CONTEXT_SIZE, { compressionMethod: "truncate" })
|
||||
|
||||
expect(manager).toBeDefined()
|
||||
})
|
||||
|
||||
it("should use default compression method when not specified", () => {
|
||||
const manager = new ContextManager(CONTEXT_SIZE, {})
|
||||
|
||||
expect(manager).toBeDefined()
|
||||
})
|
||||
|
||||
it("should accept full context config", () => {
|
||||
const manager = new ContextManager(CONTEXT_SIZE, {
|
||||
systemPromptTokens: 3000,
|
||||
maxContextUsage: 0.9,
|
||||
autoCompressAt: 0.85,
|
||||
compressionMethod: "llm-summary",
|
||||
})
|
||||
|
||||
manager.addToContext("test.ts", CONTEXT_SIZE * 0.87)
|
||||
expect(manager.needsCompression()).toBe(true)
|
||||
})
|
||||
|
||||
it("should handle edge case: autoCompressAt = 0", () => {
|
||||
const manager = new ContextManager(CONTEXT_SIZE, { autoCompressAt: 0 })
|
||||
manager.addToContext("test.ts", 1)
|
||||
|
||||
expect(manager.needsCompression()).toBe(true)
|
||||
})
|
||||
|
||||
it("should handle edge case: autoCompressAt = 1", () => {
|
||||
const manager = new ContextManager(CONTEXT_SIZE, { autoCompressAt: 1 })
|
||||
manager.addToContext("test.ts", CONTEXT_SIZE * 0.99)
|
||||
|
||||
expect(manager.needsCompression()).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -354,6 +354,36 @@ describe("RunCommandTool", () => {
|
||||
expect(execFn).toHaveBeenCalledWith("ls", expect.objectContaining({ timeout: 5000 }))
|
||||
})
|
||||
|
||||
it("should use config timeout", async () => {
|
||||
const execFn = createMockExec({})
|
||||
const toolWithMock = new RunCommandTool(undefined, execFn, { timeout: 45000 })
|
||||
const ctx = createMockContext()
|
||||
|
||||
await toolWithMock.execute({ command: "ls" }, ctx)
|
||||
|
||||
expect(execFn).toHaveBeenCalledWith("ls", expect.objectContaining({ timeout: 45000 }))
|
||||
})
|
||||
|
||||
it("should use null config timeout as default", async () => {
|
||||
const execFn = createMockExec({})
|
||||
const toolWithMock = new RunCommandTool(undefined, execFn, { timeout: null })
|
||||
const ctx = createMockContext()
|
||||
|
||||
await toolWithMock.execute({ command: "ls" }, ctx)
|
||||
|
||||
expect(execFn).toHaveBeenCalledWith("ls", expect.objectContaining({ timeout: 30000 }))
|
||||
})
|
||||
|
||||
it("should prefer param timeout over config timeout", async () => {
|
||||
const execFn = createMockExec({})
|
||||
const toolWithMock = new RunCommandTool(undefined, execFn, { timeout: 45000 })
|
||||
const ctx = createMockContext()
|
||||
|
||||
await toolWithMock.execute({ command: "ls", timeout: 5000 }, ctx)
|
||||
|
||||
expect(execFn).toHaveBeenCalledWith("ls", expect.objectContaining({ timeout: 5000 }))
|
||||
})
|
||||
|
||||
it("should execute in project root", async () => {
|
||||
const execFn = createMockExec({})
|
||||
const toolWithMock = new RunCommandTool(undefined, execFn)
|
||||
|
||||
204
packages/ipuaro/tests/unit/shared/autocomplete-config.test.ts
Normal file
204
packages/ipuaro/tests/unit/shared/autocomplete-config.test.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* Tests for AutocompleteConfigSchema.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest"
|
||||
import { AutocompleteConfigSchema } from "../../../src/shared/constants/config.js"
|
||||
|
||||
describe("AutocompleteConfigSchema", () => {
|
||||
describe("default values", () => {
|
||||
it("should use defaults when empty object provided", () => {
|
||||
const result = AutocompleteConfigSchema.parse({})
|
||||
|
||||
expect(result).toEqual({
|
||||
enabled: true,
|
||||
source: "redis-index",
|
||||
maxSuggestions: 10,
|
||||
})
|
||||
})
|
||||
|
||||
it("should use defaults via .default({})", () => {
|
||||
const result = AutocompleteConfigSchema.default({}).parse({})
|
||||
|
||||
expect(result).toEqual({
|
||||
enabled: true,
|
||||
source: "redis-index",
|
||||
maxSuggestions: 10,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("enabled", () => {
|
||||
it("should accept true", () => {
|
||||
const result = AutocompleteConfigSchema.parse({ enabled: true })
|
||||
expect(result.enabled).toBe(true)
|
||||
})
|
||||
|
||||
it("should accept false", () => {
|
||||
const result = AutocompleteConfigSchema.parse({ enabled: false })
|
||||
expect(result.enabled).toBe(false)
|
||||
})
|
||||
|
||||
it("should reject non-boolean", () => {
|
||||
expect(() => AutocompleteConfigSchema.parse({ enabled: "true" })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject number", () => {
|
||||
expect(() => AutocompleteConfigSchema.parse({ enabled: 1 })).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("source", () => {
|
||||
it("should accept redis-index", () => {
|
||||
const result = AutocompleteConfigSchema.parse({ source: "redis-index" })
|
||||
expect(result.source).toBe("redis-index")
|
||||
})
|
||||
|
||||
it("should accept filesystem", () => {
|
||||
const result = AutocompleteConfigSchema.parse({ source: "filesystem" })
|
||||
expect(result.source).toBe("filesystem")
|
||||
})
|
||||
|
||||
it("should accept both", () => {
|
||||
const result = AutocompleteConfigSchema.parse({ source: "both" })
|
||||
expect(result.source).toBe("both")
|
||||
})
|
||||
|
||||
it("should use default redis-index", () => {
|
||||
const result = AutocompleteConfigSchema.parse({})
|
||||
expect(result.source).toBe("redis-index")
|
||||
})
|
||||
|
||||
it("should reject invalid source", () => {
|
||||
expect(() => AutocompleteConfigSchema.parse({ source: "invalid" })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject non-string", () => {
|
||||
expect(() => AutocompleteConfigSchema.parse({ source: 123 })).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("maxSuggestions", () => {
|
||||
it("should accept valid positive integer", () => {
|
||||
const result = AutocompleteConfigSchema.parse({ maxSuggestions: 5 })
|
||||
expect(result.maxSuggestions).toBe(5)
|
||||
})
|
||||
|
||||
it("should accept default value", () => {
|
||||
const result = AutocompleteConfigSchema.parse({ maxSuggestions: 10 })
|
||||
expect(result.maxSuggestions).toBe(10)
|
||||
})
|
||||
|
||||
it("should accept large value", () => {
|
||||
const result = AutocompleteConfigSchema.parse({ maxSuggestions: 100 })
|
||||
expect(result.maxSuggestions).toBe(100)
|
||||
})
|
||||
|
||||
it("should accept 1", () => {
|
||||
const result = AutocompleteConfigSchema.parse({ maxSuggestions: 1 })
|
||||
expect(result.maxSuggestions).toBe(1)
|
||||
})
|
||||
|
||||
it("should reject zero", () => {
|
||||
expect(() => AutocompleteConfigSchema.parse({ maxSuggestions: 0 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject negative number", () => {
|
||||
expect(() => AutocompleteConfigSchema.parse({ maxSuggestions: -5 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject float", () => {
|
||||
expect(() => AutocompleteConfigSchema.parse({ maxSuggestions: 10.5 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject non-number", () => {
|
||||
expect(() => AutocompleteConfigSchema.parse({ maxSuggestions: "10" })).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("partial config", () => {
|
||||
it("should merge partial config with defaults (enabled only)", () => {
|
||||
const result = AutocompleteConfigSchema.parse({
|
||||
enabled: false,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
enabled: false,
|
||||
source: "redis-index",
|
||||
maxSuggestions: 10,
|
||||
})
|
||||
})
|
||||
|
||||
it("should merge partial config with defaults (source only)", () => {
|
||||
const result = AutocompleteConfigSchema.parse({
|
||||
source: "filesystem",
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
enabled: true,
|
||||
source: "filesystem",
|
||||
maxSuggestions: 10,
|
||||
})
|
||||
})
|
||||
|
||||
it("should merge partial config with defaults (maxSuggestions only)", () => {
|
||||
const result = AutocompleteConfigSchema.parse({
|
||||
maxSuggestions: 20,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
enabled: true,
|
||||
source: "redis-index",
|
||||
maxSuggestions: 20,
|
||||
})
|
||||
})
|
||||
|
||||
it("should merge multiple partial fields", () => {
|
||||
const result = AutocompleteConfigSchema.parse({
|
||||
enabled: false,
|
||||
maxSuggestions: 5,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
enabled: false,
|
||||
source: "redis-index",
|
||||
maxSuggestions: 5,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("full config", () => {
|
||||
it("should accept valid full config", () => {
|
||||
const config = {
|
||||
enabled: false,
|
||||
source: "both" as const,
|
||||
maxSuggestions: 15,
|
||||
}
|
||||
|
||||
const result = AutocompleteConfigSchema.parse(config)
|
||||
expect(result).toEqual(config)
|
||||
})
|
||||
|
||||
it("should accept all defaults explicitly", () => {
|
||||
const config = {
|
||||
enabled: true,
|
||||
source: "redis-index" as const,
|
||||
maxSuggestions: 10,
|
||||
}
|
||||
|
||||
const result = AutocompleteConfigSchema.parse(config)
|
||||
expect(result).toEqual(config)
|
||||
})
|
||||
|
||||
it("should accept filesystem as source", () => {
|
||||
const config = {
|
||||
enabled: true,
|
||||
source: "filesystem" as const,
|
||||
maxSuggestions: 20,
|
||||
}
|
||||
|
||||
const result = AutocompleteConfigSchema.parse(config)
|
||||
expect(result).toEqual(config)
|
||||
})
|
||||
})
|
||||
})
|
||||
137
packages/ipuaro/tests/unit/shared/commands-config.test.ts
Normal file
137
packages/ipuaro/tests/unit/shared/commands-config.test.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Tests for CommandsConfigSchema.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest"
|
||||
import { CommandsConfigSchema } from "../../../src/shared/constants/config.js"
|
||||
|
||||
describe("CommandsConfigSchema", () => {
|
||||
describe("default values", () => {
|
||||
it("should use defaults when empty object provided", () => {
|
||||
const result = CommandsConfigSchema.parse({})
|
||||
|
||||
expect(result).toEqual({
|
||||
timeout: null,
|
||||
})
|
||||
})
|
||||
|
||||
it("should use defaults via .default({})", () => {
|
||||
const result = CommandsConfigSchema.default({}).parse({})
|
||||
|
||||
expect(result).toEqual({
|
||||
timeout: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("timeout", () => {
|
||||
it("should accept null (default)", () => {
|
||||
const result = CommandsConfigSchema.parse({ timeout: null })
|
||||
expect(result.timeout).toBe(null)
|
||||
})
|
||||
|
||||
it("should accept positive integer", () => {
|
||||
const result = CommandsConfigSchema.parse({ timeout: 5000 })
|
||||
expect(result.timeout).toBe(5000)
|
||||
})
|
||||
|
||||
it("should accept large timeout", () => {
|
||||
const result = CommandsConfigSchema.parse({ timeout: 600000 })
|
||||
expect(result.timeout).toBe(600000)
|
||||
})
|
||||
|
||||
it("should accept 1", () => {
|
||||
const result = CommandsConfigSchema.parse({ timeout: 1 })
|
||||
expect(result.timeout).toBe(1)
|
||||
})
|
||||
|
||||
it("should accept small timeout", () => {
|
||||
const result = CommandsConfigSchema.parse({ timeout: 100 })
|
||||
expect(result.timeout).toBe(100)
|
||||
})
|
||||
|
||||
it("should reject zero", () => {
|
||||
expect(() => CommandsConfigSchema.parse({ timeout: 0 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject negative number", () => {
|
||||
expect(() => CommandsConfigSchema.parse({ timeout: -5000 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject float", () => {
|
||||
expect(() => CommandsConfigSchema.parse({ timeout: 5000.5 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject string", () => {
|
||||
expect(() => CommandsConfigSchema.parse({ timeout: "5000" })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject boolean", () => {
|
||||
expect(() => CommandsConfigSchema.parse({ timeout: true })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject undefined (use null instead)", () => {
|
||||
const result = CommandsConfigSchema.parse({ timeout: undefined })
|
||||
expect(result.timeout).toBe(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe("partial config", () => {
|
||||
it("should use default null when timeout not provided", () => {
|
||||
const result = CommandsConfigSchema.parse({})
|
||||
|
||||
expect(result).toEqual({
|
||||
timeout: null,
|
||||
})
|
||||
})
|
||||
|
||||
it("should accept explicit null", () => {
|
||||
const result = CommandsConfigSchema.parse({
|
||||
timeout: null,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
timeout: null,
|
||||
})
|
||||
})
|
||||
|
||||
it("should accept explicit timeout value", () => {
|
||||
const result = CommandsConfigSchema.parse({
|
||||
timeout: 10000,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
timeout: 10000,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("full config", () => {
|
||||
it("should accept valid config with null", () => {
|
||||
const config = {
|
||||
timeout: null,
|
||||
}
|
||||
|
||||
const result = CommandsConfigSchema.parse(config)
|
||||
expect(result).toEqual(config)
|
||||
})
|
||||
|
||||
it("should accept valid config with timeout", () => {
|
||||
const config = {
|
||||
timeout: 30000,
|
||||
}
|
||||
|
||||
const result = CommandsConfigSchema.parse(config)
|
||||
expect(result).toEqual(config)
|
||||
})
|
||||
|
||||
it("should accept default explicitly", () => {
|
||||
const config = {
|
||||
timeout: null,
|
||||
}
|
||||
|
||||
const result = CommandsConfigSchema.parse(config)
|
||||
expect(result).toEqual(config)
|
||||
})
|
||||
})
|
||||
})
|
||||
221
packages/ipuaro/tests/unit/shared/context-config.test.ts
Normal file
221
packages/ipuaro/tests/unit/shared/context-config.test.ts
Normal file
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* Tests for ContextConfigSchema.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest"
|
||||
import { ContextConfigSchema } from "../../../src/shared/constants/config.js"
|
||||
|
||||
describe("ContextConfigSchema", () => {
|
||||
describe("default values", () => {
|
||||
it("should use defaults when empty object provided", () => {
|
||||
const result = ContextConfigSchema.parse({})
|
||||
|
||||
expect(result).toEqual({
|
||||
systemPromptTokens: 2000,
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "llm-summary",
|
||||
})
|
||||
})
|
||||
|
||||
it("should use defaults via .default({})", () => {
|
||||
const result = ContextConfigSchema.default({}).parse({})
|
||||
|
||||
expect(result).toEqual({
|
||||
systemPromptTokens: 2000,
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "llm-summary",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("systemPromptTokens", () => {
|
||||
it("should accept valid positive integer", () => {
|
||||
const result = ContextConfigSchema.parse({ systemPromptTokens: 1500 })
|
||||
expect(result.systemPromptTokens).toBe(1500)
|
||||
})
|
||||
|
||||
it("should accept default value", () => {
|
||||
const result = ContextConfigSchema.parse({ systemPromptTokens: 2000 })
|
||||
expect(result.systemPromptTokens).toBe(2000)
|
||||
})
|
||||
|
||||
it("should accept large value", () => {
|
||||
const result = ContextConfigSchema.parse({ systemPromptTokens: 5000 })
|
||||
expect(result.systemPromptTokens).toBe(5000)
|
||||
})
|
||||
|
||||
it("should reject zero", () => {
|
||||
expect(() => ContextConfigSchema.parse({ systemPromptTokens: 0 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject negative number", () => {
|
||||
expect(() => ContextConfigSchema.parse({ systemPromptTokens: -100 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject float", () => {
|
||||
expect(() => ContextConfigSchema.parse({ systemPromptTokens: 1500.5 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject non-number", () => {
|
||||
expect(() => ContextConfigSchema.parse({ systemPromptTokens: "2000" })).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("maxContextUsage", () => {
|
||||
it("should accept valid ratio", () => {
|
||||
const result = ContextConfigSchema.parse({ maxContextUsage: 0.7 })
|
||||
expect(result.maxContextUsage).toBe(0.7)
|
||||
})
|
||||
|
||||
it("should accept default value", () => {
|
||||
const result = ContextConfigSchema.parse({ maxContextUsage: 0.8 })
|
||||
expect(result.maxContextUsage).toBe(0.8)
|
||||
})
|
||||
|
||||
it("should accept minimum value (0)", () => {
|
||||
const result = ContextConfigSchema.parse({ maxContextUsage: 0 })
|
||||
expect(result.maxContextUsage).toBe(0)
|
||||
})
|
||||
|
||||
it("should accept maximum value (1)", () => {
|
||||
const result = ContextConfigSchema.parse({ maxContextUsage: 1 })
|
||||
expect(result.maxContextUsage).toBe(1)
|
||||
})
|
||||
|
||||
it("should reject value above 1", () => {
|
||||
expect(() => ContextConfigSchema.parse({ maxContextUsage: 1.1 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject negative value", () => {
|
||||
expect(() => ContextConfigSchema.parse({ maxContextUsage: -0.1 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject non-number", () => {
|
||||
expect(() => ContextConfigSchema.parse({ maxContextUsage: "0.8" })).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("autoCompressAt", () => {
|
||||
it("should accept valid ratio", () => {
|
||||
const result = ContextConfigSchema.parse({ autoCompressAt: 0.75 })
|
||||
expect(result.autoCompressAt).toBe(0.75)
|
||||
})
|
||||
|
||||
it("should accept default value", () => {
|
||||
const result = ContextConfigSchema.parse({ autoCompressAt: 0.8 })
|
||||
expect(result.autoCompressAt).toBe(0.8)
|
||||
})
|
||||
|
||||
it("should accept minimum value (0)", () => {
|
||||
const result = ContextConfigSchema.parse({ autoCompressAt: 0 })
|
||||
expect(result.autoCompressAt).toBe(0)
|
||||
})
|
||||
|
||||
it("should accept maximum value (1)", () => {
|
||||
const result = ContextConfigSchema.parse({ autoCompressAt: 1 })
|
||||
expect(result.autoCompressAt).toBe(1)
|
||||
})
|
||||
|
||||
it("should reject value above 1", () => {
|
||||
expect(() => ContextConfigSchema.parse({ autoCompressAt: 1.5 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject negative value", () => {
|
||||
expect(() => ContextConfigSchema.parse({ autoCompressAt: -0.5 })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject non-number", () => {
|
||||
expect(() => ContextConfigSchema.parse({ autoCompressAt: "0.8" })).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("compressionMethod", () => {
|
||||
it("should accept llm-summary", () => {
|
||||
const result = ContextConfigSchema.parse({ compressionMethod: "llm-summary" })
|
||||
expect(result.compressionMethod).toBe("llm-summary")
|
||||
})
|
||||
|
||||
it("should accept truncate", () => {
|
||||
const result = ContextConfigSchema.parse({ compressionMethod: "truncate" })
|
||||
expect(result.compressionMethod).toBe("truncate")
|
||||
})
|
||||
|
||||
it("should reject invalid method", () => {
|
||||
expect(() => ContextConfigSchema.parse({ compressionMethod: "invalid" })).toThrow()
|
||||
})
|
||||
|
||||
it("should reject non-string", () => {
|
||||
expect(() => ContextConfigSchema.parse({ compressionMethod: 123 })).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("partial config", () => {
|
||||
it("should merge partial config with defaults (systemPromptTokens)", () => {
|
||||
const result = ContextConfigSchema.parse({
|
||||
systemPromptTokens: 3000,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
systemPromptTokens: 3000,
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "llm-summary",
|
||||
})
|
||||
})
|
||||
|
||||
it("should merge partial config with defaults (autoCompressAt)", () => {
|
||||
const result = ContextConfigSchema.parse({
|
||||
autoCompressAt: 0.9,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
systemPromptTokens: 2000,
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.9,
|
||||
compressionMethod: "llm-summary",
|
||||
})
|
||||
})
|
||||
|
||||
it("should merge multiple partial fields", () => {
|
||||
const result = ContextConfigSchema.parse({
|
||||
maxContextUsage: 0.7,
|
||||
compressionMethod: "truncate",
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
systemPromptTokens: 2000,
|
||||
maxContextUsage: 0.7,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "truncate",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("full config", () => {
|
||||
it("should accept valid full config", () => {
|
||||
const config = {
|
||||
systemPromptTokens: 3000,
|
||||
maxContextUsage: 0.9,
|
||||
autoCompressAt: 0.85,
|
||||
compressionMethod: "truncate" as const,
|
||||
}
|
||||
|
||||
const result = ContextConfigSchema.parse(config)
|
||||
expect(result).toEqual(config)
|
||||
})
|
||||
|
||||
it("should accept all defaults explicitly", () => {
|
||||
const config = {
|
||||
systemPromptTokens: 2000,
|
||||
maxContextUsage: 0.8,
|
||||
autoCompressAt: 0.8,
|
||||
compressionMethod: "llm-summary" as const,
|
||||
}
|
||||
|
||||
const result = ContextConfigSchema.parse(config)
|
||||
expect(result).toEqual(config)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user