mirror of
https://github.com/safedep/vet.git
synced 2025-12-10 13:43:01 -06:00
* Add initial UI for agent mode * fix: Cleanup and define agent contract * Add react agent * Add interactions memory * Add support for stdio based MCP integration * Add basic sqlite3 report generator * fix: Persist vulnerabilities with package relation * fix: Persist license information * refactor: Agents into its own command package * feat: Add support for tool calling introspection * refactor: UI to hide implementation detail * sqlite3 reporter persist dependency graph * fix: Support multiple LLM provider for agent * docs: Update agents doc * docs: Remove deprecated query docs * fix: UI tests * fix: Linter issue * Add support for prompt mode * Improve UI with animation * Fix UI tests after update * Add OpenSSF scorecard persistence * Add slsa provenances in sqlite3 reporter * Add test cases for sqlite3 reporter * Fix agent doc * fix: Sqlite3 reporter use safe accessors * feat: Add support for fast model * feat: Simplify and streamline agent UI for better user experience - Remove decorative borders and excessive styling to maximize output area - Implement clean minimal design similar to modern TUI interfaces - Add bordered input area for clear visual separation - Move thinking indicator above input area for better visibility - Enhance input field reset logic for proper line alignment - Remove verbose help text and status messages - Optimize layout calculations for full width utilization - Add smooth animations for agent thinking state with spinner - Clean up code structure and remove unused progress bar functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Improve agent status line * test: Update UI tests * fix: Use terminal safe rendering * fix: Fix nil deref without storing empty strings in DB * fix: Support overwriting sqlite3 database * fix: Data model to use m2m between manifest and package * style: Fix linter issue with unused variables * Misc fixes * Add test for agent memory --------- Co-authored-by: Claude <noreply@anthropic.com>
280 lines
6.8 KiB
Go
280 lines
6.8 KiB
Go
package agent
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/cloudwego/eino/schema"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNewSimpleMemory(t *testing.T) {
|
|
memory, err := NewSimpleMemory()
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, memory)
|
|
|
|
// Test that initial interactions are empty
|
|
ctx := context.Background()
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(interactions))
|
|
}
|
|
|
|
func TestSimpleMemory_AddInteraction(t *testing.T) {
|
|
memory, err := NewSimpleMemory()
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
message := &schema.Message{
|
|
Role: schema.User,
|
|
Content: "test message",
|
|
}
|
|
|
|
err = memory.AddInteraction(ctx, message)
|
|
assert.NoError(t, err)
|
|
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(interactions))
|
|
assert.Equal(t, message, interactions[0])
|
|
}
|
|
|
|
func TestSimpleMemory_AddMultipleInteractions(t *testing.T) {
|
|
memory, err := NewSimpleMemory()
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
messages := []*schema.Message{
|
|
{Role: schema.User, Content: "first message"},
|
|
{Role: schema.Assistant, Content: "second message"},
|
|
{Role: schema.User, Content: "third message"},
|
|
}
|
|
|
|
for _, msg := range messages {
|
|
err = memory.AddInteraction(ctx, msg)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 3, len(interactions))
|
|
|
|
for i, msg := range messages {
|
|
assert.Equal(t, msg, interactions[i])
|
|
}
|
|
}
|
|
|
|
func TestSimpleMemory_GetInteractions(t *testing.T) {
|
|
memory, err := NewSimpleMemory()
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Test empty interactions
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, interactions)
|
|
assert.Equal(t, 0, len(interactions))
|
|
|
|
// Add some interactions
|
|
message1 := &schema.Message{Role: schema.User, Content: "message 1"}
|
|
message2 := &schema.Message{Role: schema.Assistant, Content: "message 2"}
|
|
|
|
err = memory.AddInteraction(ctx, message1)
|
|
require.NoError(t, err)
|
|
err = memory.AddInteraction(ctx, message2)
|
|
require.NoError(t, err)
|
|
|
|
interactions, err = memory.GetInteractions(ctx)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 2, len(interactions))
|
|
assert.Equal(t, message1, interactions[0])
|
|
assert.Equal(t, message2, interactions[1])
|
|
}
|
|
|
|
func TestSimpleMemory_Clear(t *testing.T) {
|
|
memory, err := NewSimpleMemory()
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Add some interactions
|
|
message1 := &schema.Message{Role: schema.User, Content: "message 1"}
|
|
message2 := &schema.Message{Role: schema.Assistant, Content: "message 2"}
|
|
|
|
err = memory.AddInteraction(ctx, message1)
|
|
require.NoError(t, err)
|
|
err = memory.AddInteraction(ctx, message2)
|
|
require.NoError(t, err)
|
|
|
|
// Verify interactions exist
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(interactions))
|
|
|
|
// Clear interactions
|
|
err = memory.Clear(ctx)
|
|
assert.NoError(t, err)
|
|
|
|
// Verify interactions are cleared
|
|
interactions, err = memory.GetInteractions(ctx)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 0, len(interactions))
|
|
}
|
|
|
|
func TestSimpleMemory_NilInteraction(t *testing.T) {
|
|
memory, err := NewSimpleMemory()
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Test adding nil interaction
|
|
err = memory.AddInteraction(ctx, nil)
|
|
assert.NoError(t, err)
|
|
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(interactions))
|
|
assert.Nil(t, interactions[0])
|
|
}
|
|
|
|
func TestSimpleMemory_ConcurrentAccess(t *testing.T) {
|
|
memory, err := NewSimpleMemory()
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
numGoroutines := 100
|
|
messagesPerGoroutine := 10
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(numGoroutines)
|
|
|
|
// Concurrent writes
|
|
for i := 0; i < numGoroutines; i++ {
|
|
go func(goroutineID int) {
|
|
defer wg.Done()
|
|
for j := 0; j < messagesPerGoroutine; j++ {
|
|
message := &schema.Message{
|
|
Role: schema.User,
|
|
Content: fmt.Sprintf("goroutine-%d-message-%d", goroutineID, j),
|
|
}
|
|
err := memory.AddInteraction(ctx, message)
|
|
assert.NoError(t, err)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
// Verify all interactions were added
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, numGoroutines*messagesPerGoroutine, len(interactions))
|
|
}
|
|
|
|
func TestSimpleMemory_ConcurrentReadWrite(t *testing.T) {
|
|
memory, err := NewSimpleMemory()
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
numReaders := 10
|
|
numWriters := 10
|
|
messagesPerWriter := 5
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(numReaders + numWriters)
|
|
|
|
// Concurrent writers
|
|
for i := 0; i < numWriters; i++ {
|
|
go func(writerID int) {
|
|
defer wg.Done()
|
|
for j := 0; j < messagesPerWriter; j++ {
|
|
message := &schema.Message{
|
|
Role: schema.User,
|
|
Content: fmt.Sprintf("writer-%d-message-%d", writerID, j),
|
|
}
|
|
err := memory.AddInteraction(ctx, message)
|
|
assert.NoError(t, err)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
// Concurrent readers
|
|
for i := 0; i < numReaders; i++ {
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < messagesPerWriter; j++ {
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, interactions)
|
|
// Length can vary due to concurrent writes
|
|
assert.GreaterOrEqual(t, len(interactions), 0)
|
|
}
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
// Final verification
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, numWriters*messagesPerWriter, len(interactions))
|
|
}
|
|
|
|
func TestSimpleMemory_ClearDuringConcurrentAccess(t *testing.T) {
|
|
memory, err := NewSimpleMemory()
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
numWriters := 5
|
|
messagesPerWriter := 10
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(numWriters + 1) // +1 for the clearer
|
|
|
|
// Add some initial interactions
|
|
for i := 0; i < 5; i++ {
|
|
message := &schema.Message{
|
|
Role: schema.User,
|
|
Content: fmt.Sprintf("initial-message-%d", i),
|
|
}
|
|
err := memory.AddInteraction(ctx, message)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Concurrent writers
|
|
for i := 0; i < numWriters; i++ {
|
|
go func(writerID int) {
|
|
defer wg.Done()
|
|
for j := 0; j < messagesPerWriter; j++ {
|
|
message := &schema.Message{
|
|
Role: schema.User,
|
|
Content: fmt.Sprintf("writer-%d-message-%d", writerID, j),
|
|
}
|
|
err := memory.AddInteraction(ctx, message)
|
|
assert.NoError(t, err)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
// Clear operation
|
|
go func() {
|
|
defer wg.Done()
|
|
// Clear after some writes have happened
|
|
err := memory.Clear(ctx)
|
|
assert.NoError(t, err)
|
|
}()
|
|
|
|
wg.Wait()
|
|
|
|
// Final state check - should be consistent
|
|
interactions, err := memory.GetInteractions(ctx)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, interactions)
|
|
// The exact number depends on timing of clear operation
|
|
assert.GreaterOrEqual(t, len(interactions), 0)
|
|
}
|