lc-cli 0.1.3

LLM Client - A fast Rust-based LLM CLI tool with provider management and chat sessions
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
//! Integration tests for similar commands
//!
//! This module contains comprehensive integration tests for all similarity search-related
//! CLI commands, testing the underlying functionality as the CLI would use it.

mod common;

use lc::config::Config;
use lc::vector_db::VectorDatabase;
use std::collections::HashMap;

#[cfg(test)]
mod similar_search_tests {
    use super::*;

    fn setup_test_database_with_vectors(test_name: &str) -> VectorDatabase {
        let db_name = format!("similar_test_{}_{}", test_name, std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        let model = "text-embedding-3-small";
        let provider = "openai";

        // Add test vectors with known relationships
        let test_data = vec![
            // AI/ML related (should be similar to each other)
            (
                "Machine learning is a subset of artificial intelligence",
                vec![0.8, 0.6, 0.2, 0.1, 0.0],
            ),
            (
                "Deep learning uses neural networks with multiple layers",
                vec![0.7, 0.7, 0.3, 0.1, 0.0],
            ),
            (
                "Artificial intelligence enables computers to think",
                vec![0.9, 0.5, 0.1, 0.1, 0.0],
            ),
            // Programming related (different cluster)
            (
                "Python is a popular programming language",
                vec![0.1, 0.2, 0.8, 0.7, 0.1],
            ),
            (
                "JavaScript runs in web browsers",
                vec![0.0, 0.1, 0.9, 0.6, 0.2],
            ),
            // Completely different topic
            (
                "Cooking pasta requires boiling water",
                vec![0.0, 0.0, 0.1, 0.1, 0.9],
            ),
        ];

        for (text, vector) in test_data {
            db.add_vector(text, &vector, model, provider).unwrap();
        }

        db
    }

    #[test]
    fn test_basic_similarity_search() {
        let db = setup_test_database_with_vectors("basic");

        // Query with AI-related vector
        let ai_query = vec![0.85, 0.55, 0.15, 0.1, 0.0];
        let result = db.find_similar(&ai_query, 3);
        assert!(result.is_ok());

        let similar = result.unwrap();
        assert_eq!(similar.len(), 3);

        // Results should be ordered by similarity (highest first)
        assert!(similar[0].1 >= similar[1].1);
        assert!(similar[1].1 >= similar[2].1);

        // First result should be most similar to AI content
        assert!(
            similar[0].0.text.to_lowercase().contains("artificial")
                || similar[0].0.text.to_lowercase().contains("machine")
                || similar[0].0.text.to_lowercase().contains("deep")
        );
    }

    #[test]
    fn test_similarity_search_with_limit() {
        let db = setup_test_database_with_vectors("with_limit");

        let query = vec![0.5, 0.5, 0.5, 0.5, 0.5];

        // Test different limits
        for limit in 1..=6 {
            let result = db.find_similar(&query, limit);
            assert!(result.is_ok());

            let similar = result.unwrap();
            let expected_len = std::cmp::min(limit, 6); // We have 6 vectors total
            assert_eq!(similar.len(), expected_len);
        }
    }

    #[test]
    fn test_similarity_search_with_zero_limit() {
        let db = setup_test_database_with_vectors("zero_limit");

        let query = vec![0.5, 0.5, 0.5, 0.5, 0.5];
        let result = db.find_similar(&query, 0);
        assert!(result.is_ok());

        let similar = result.unwrap();
        assert_eq!(similar.len(), 0);
    }

    #[test]
    fn test_similarity_search_exact_match() {
        let db = setup_test_database_with_vectors("exact_match");

        // Use exact vector from our test data
        let exact_query = vec![0.8, 0.6, 0.2, 0.1, 0.0];
        let result = db.find_similar(&exact_query, 1);
        assert!(result.is_ok());

        let similar = result.unwrap();
        assert_eq!(similar.len(), 1);

        // Should have very high similarity (close to 1.0)
        assert!(similar[0].1 > 0.95);
        assert!(similar[0].0.text.contains("Machine learning"));
    }

    #[test]
    fn test_similarity_search_ordering() {
        let db = setup_test_database_with_vectors("ordering");

        let query = vec![0.8, 0.6, 0.2, 0.1, 0.0]; // AI-related query
        let result = db.find_similar(&query, 6);
        assert!(result.is_ok());

        let similar = result.unwrap();
        assert_eq!(similar.len(), 6);

        // Verify ordering (similarity scores should be descending)
        for i in 1..similar.len() {
            assert!(
                similar[i - 1].1 >= similar[i].1,
                "Similarity scores not in descending order: {} >= {}",
                similar[i - 1].1,
                similar[i].1
            );
        }

        // AI-related content should be at the top
        let top_results = &similar[0..3];
        let ai_keywords = [
            "machine",
            "learning",
            "artificial",
            "intelligence",
            "deep",
            "neural",
        ];

        for (entry, _) in top_results {
            let text_lower = entry.text.to_lowercase();
            let has_ai_keyword = ai_keywords
                .iter()
                .any(|&keyword| text_lower.contains(keyword));
            assert!(
                has_ai_keyword,
                "Top result should contain AI-related keywords: {}",
                entry.text
            );
        }
    }

    #[test]
    fn test_similarity_search_empty_database() {
        let db_name = format!("empty_similar_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();

        let query = vec![1.0, 0.0, 0.0];
        let result = db.find_similar(&query, 5);
        assert!(result.is_ok());

        let similar = result.unwrap();
        assert!(similar.is_empty());

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }

    #[test]
    fn test_similarity_search_single_vector() {
        let db_name = format!("single_similar_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        let model = "text-embedding-3-small";
        let provider = "openai";

        // Add single vector
        let vector = vec![0.5, 0.5, 0.5];
        db.add_vector("Single vector", &vector, model, provider)
            .unwrap();

        // Search should return that single vector
        let query = vec![0.6, 0.4, 0.5];
        let result = db.find_similar(&query, 5);
        assert!(result.is_ok());

        let similar = result.unwrap();
        assert_eq!(similar.len(), 1);
        assert_eq!(similar[0].0.text, "Single vector");

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }
}

#[cfg(test)]
mod similar_model_resolution_tests {
    use super::*;

    fn create_test_config_for_similarity() -> Config {
        let mut config = Config {
            providers: HashMap::new(),
            default_provider: Some("openai".to_string()),
            default_model: Some("text-embedding-3-small".to_string()),
            aliases: HashMap::new(),
            system_prompt: None,
            templates: HashMap::new(),
            max_tokens: None,
            temperature: None,
            stream: None,
        };

        // Add OpenAI provider
        config.providers.insert(
            "openai".to_string(),
            lc::config::ProviderConfig {
                endpoint: "https://api.openai.com/v1".to_string(),
                api_key: Some("sk-test123".to_string()),
                models: vec![
                    "text-embedding-3-small".to_string(),
                    "text-embedding-3-large".to_string(),
                ],
                models_path: "/v1/models".to_string(),
                chat_path: "/v1/chat/completions".to_string(),
                headers: HashMap::new(),
                token_url: None,
                cached_token: None,
                auth_type: None,
                vars: HashMap::new(),
                images_path: Some("/images/generations".to_string()),
                embeddings_path: Some("/embeddings".to_string()),
                chat_templates: None,
                images_templates: None,
                embeddings_templates: None,
                models_templates: None,
                audio_path: None,
                speech_path: None,
                audio_templates: None,
                speech_templates: None,
            },
        );

        // Add Cohere provider
        config.providers.insert(
            "cohere".to_string(),
            lc::config::ProviderConfig {
                endpoint: "https://api.cohere.ai/v1".to_string(),
                api_key: Some("cohere-test-key".to_string()),
                models: vec!["embed-english-v3.0".to_string()],
                models_path: "/v1/models".to_string(),
                chat_path: "/v1/chat".to_string(),
                headers: HashMap::new(),
                token_url: None,
                cached_token: None,
                auth_type: None,
                vars: HashMap::new(),
                images_path: Some("/images/generations".to_string()),
                embeddings_path: Some("/embeddings".to_string()),
                chat_templates: None,
                images_templates: None,
                embeddings_templates: None,
                models_templates: None,
                audio_path: None,
                speech_path: None,
                audio_templates: None,
                speech_templates: None,
            },
        );

        config
    }

    #[test]
    fn test_similar_model_resolution_from_database() {
        let config = create_test_config_for_similarity();

        // Create database with specific model
        let db_name = format!("model_resolution_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        let stored_model = "text-embedding-3-large";
        let stored_provider = "openai";

        // Add vector to establish model info
        let vector = vec![0.1, 0.2, 0.3];
        db.add_vector("Test text", &vector, stored_model, stored_provider)
            .unwrap();

        // Get model info from database
        let model_info = db.get_model_info().unwrap();
        assert!(model_info.is_some());

        let (db_model, db_provider) = model_info.unwrap();
        assert_eq!(db_model, stored_model);
        assert_eq!(db_provider, stored_provider);

        // Test model resolution when no explicit model provided
        let result =
            lc::utils::resolve_model_and_provider(&config, Some(db_provider), Some(db_model));
        assert!(result.is_ok());

        let (resolved_provider, resolved_model) = result.unwrap();
        assert_eq!(resolved_provider, stored_provider);
        assert_eq!(resolved_model, stored_model);

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }

    #[test]
    fn test_similar_model_override() {
        let config = create_test_config_for_similarity();

        // Create database with one model
        let db_name = format!("model_override_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        db.add_vector(
            "Test",
            &vec![0.1, 0.2, 0.3],
            "text-embedding-3-small",
            "openai",
        )
        .unwrap();

        // Test explicit model override
        let result = lc::utils::resolve_model_and_provider(
            &config,
            Some("cohere".to_string()),
            Some("embed-english-v3.0".to_string()),
        );
        assert!(result.is_ok());

        let (provider, model) = result.unwrap();
        assert_eq!(provider, "cohere");
        assert_eq!(model, "embed-english-v3.0");

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }

    #[test]
    fn test_similar_with_provider_model_format() {
        let config = create_test_config_for_similarity();

        // Test provider:model format
        let result = lc::utils::resolve_model_and_provider(
            &config,
            None,
            Some("cohere:embed-english-v3.0".to_string()),
        );
        assert!(result.is_ok());

        let (provider, model) = result.unwrap();
        assert_eq!(provider, "cohere");
        assert_eq!(model, "embed-english-v3.0");
    }
}

#[cfg(test)]
mod similar_parameter_validation_tests {
    use super::*;

    #[test]
    fn test_similarity_limit_validation() {
        let db_name = format!("limit_validation_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        let model = "text-embedding-3-small";
        let provider = "openai";

        // Add some test vectors
        for i in 0..5 {
            let vector = vec![i as f64, 0.0, 0.0];
            db.add_vector(&format!("Text {}", i), &vector, model, provider)
                .unwrap();
        }

        let query = vec![0.0, 0.0, 0.0];

        // Test various limit values
        let test_limits = vec![0, 1, 3, 5, 10, 100];

        for limit in test_limits {
            let result = db.find_similar(&query, limit);
            assert!(result.is_ok(), "Failed with limit: {}", limit);

            let similar = result.unwrap();
            let expected_len = std::cmp::min(limit, 5); // We have 5 vectors
            assert_eq!(
                similar.len(),
                expected_len,
                "Wrong result count for limit: {}",
                limit
            );
        }

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }

    #[test]
    fn test_similarity_query_vector_validation() {
        let db_name = format!("query_validation_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        let model = "text-embedding-3-small";
        let provider = "openai";

        // Add test vector
        let stored_vector = vec![1.0, 0.0, 0.0];
        db.add_vector("Test vector", &stored_vector, model, provider)
            .unwrap();

        // Test various query vectors
        let test_queries = vec![
            vec![1.0, 0.0, 0.0],      // Same dimensions
            vec![0.5, 0.5, 0.5],      // Same dimensions, different values
            vec![1.0, 0.0],           // Different dimensions (fewer)
            vec![1.0, 0.0, 0.0, 0.0], // Different dimensions (more)
            vec![],                   // Empty vector
        ];

        for (i, query) in test_queries.iter().enumerate() {
            let result = db.find_similar(query, 1);
            // The behavior depends on implementation
            // Some might handle dimension mismatches, others might error
            match result {
                Ok(similar) => {
                    // If it succeeds, should return valid results
                    assert!(similar.len() <= 1, "Query {} returned too many results", i);
                }
                Err(_) => {
                    // Error is acceptable for invalid queries
                }
            }
        }

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }

    #[test]
    fn test_similarity_with_special_values() {
        let db_name = format!("special_values_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        let model = "text-embedding-3-small";
        let provider = "openai";

        // Add normal vector
        let normal_vector = vec![0.5, 0.5, 0.5];
        db.add_vector("Normal vector", &normal_vector, model, provider)
            .unwrap();

        // Test queries with special values
        let special_queries = vec![
            vec![0.0, 0.0, 0.0],      // All zeros
            vec![1.0, 1.0, 1.0],      // All ones
            vec![-1.0, -1.0, -1.0],   // Negative values
            vec![f64::MAX, 0.0, 0.0], // Very large value
            vec![f64::MIN, 0.0, 0.0], // Very small value
        ];

        for query in special_queries {
            let result = db.find_similar(&query, 1);
            // Should handle special values gracefully
            match result {
                Ok(similar) => {
                    assert!(similar.len() <= 1);
                    if !similar.is_empty() {
                        // Similarity score should be finite
                        assert!(
                            similar[0].1.is_finite(),
                            "Similarity score should be finite for query: {:?}",
                            query
                        );
                    }
                }
                Err(_) => {
                    // Error handling is also acceptable
                }
            }
        }

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }
}

#[cfg(test)]
mod similar_error_handling_tests {
    use super::*;

    #[test]
    fn test_similar_with_nonexistent_database() {
        // Try to search in a database that doesn't exist
        let db_name = format!("nonexistent_similar_db_{}", std::process::id());
        let result = VectorDatabase::new(&db_name);

        // This might succeed (creating the database) or fail
        match result {
            Ok(db) => {
                // If it creates the database, it should be empty
                let query = vec![1.0, 0.0, 0.0];
                let similar_result = db.find_similar(&query, 5);
                assert!(similar_result.is_ok());

                let similar = similar_result.unwrap();
                assert!(similar.is_empty());

                // Cleanup
                VectorDatabase::delete_database(&db_name).unwrap();
            }
            Err(_) => {
                // Error is also acceptable
            }
        }
    }

    #[test]
    fn test_similar_with_corrupted_data() {
        // This test would be implementation-specific
        // Testing how the system handles corrupted vector data
        let db_name = format!("corruption_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        let model = "text-embedding-3-small";
        let provider = "openai";

        // Add some normal data
        db.add_vector("Normal text", &vec![0.1, 0.2, 0.3], model, provider)
            .unwrap();

        // Normal query should work
        let query = vec![0.1, 0.2, 0.3];
        let result = db.find_similar(&query, 1);
        assert!(result.is_ok());

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }

    #[test]
    fn test_similar_with_invalid_model_info() {
        let config = Config {
            providers: HashMap::new(),
            default_provider: None,
            default_model: None,
            aliases: HashMap::new(),
            system_prompt: None,
            templates: HashMap::new(),
            max_tokens: None,
            temperature: None,
            stream: None,
        };

        // Test with empty config (no providers)
        let result = lc::utils::resolve_model_and_provider(&config, None, None);
        assert!(result.is_err());

        // Test with invalid provider
        let result = lc::utils::resolve_model_and_provider(
            &config,
            Some("invalid".to_string()),
            Some("model".to_string()),
        );
        assert!(result.is_err());
    }
}

#[cfg(test)]
mod similar_performance_tests {
    use super::*;

    #[test]
    fn test_similarity_search_performance() {
        let db_name = format!("performance_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        let model = "text-embedding-3-small";
        let provider = "openai";

        // Add many vectors
        let vector_count = 100;
        for i in 0..vector_count {
            let vector: Vec<f64> = (0..10).map(|j| (i * 10 + j) as f64 * 0.01).collect();
            db.add_vector(&format!("Vector {}", i), &vector, model, provider)
                .unwrap();
        }

        // Test search performance
        let query: Vec<f64> = (0..10).map(|i| i as f64 * 0.01).collect();

        let start = std::time::Instant::now();
        let result = db.find_similar(&query, 10);
        let duration = start.elapsed();

        assert!(result.is_ok());
        let similar = result.unwrap();
        assert_eq!(similar.len(), 10);

        // Performance should be reasonable (less than 1 second for 100 vectors)
        assert!(
            duration.as_secs() < 1,
            "Search took too long: {:?}",
            duration
        );

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }

    #[test]
    fn test_similarity_search_with_large_vectors() {
        let db_name = format!("large_vector_performance_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();
        let model = "text-embedding-3-large";
        let provider = "openai";

        // Add vectors with realistic embedding dimensions
        let dimension = 1536; // OpenAI text-embedding-3-small dimension
        let vector_count = 10;

        for i in 0..vector_count {
            let vector: Vec<f64> = (0..dimension)
                .map(|j| ((i * dimension + j) as f64) * 0.0001)
                .collect();
            db.add_vector(&format!("Large vector {}", i), &vector, model, provider)
                .unwrap();
        }

        // Test search with large query vector
        let query: Vec<f64> = (0..dimension).map(|i| (i as f64) * 0.0001).collect();

        let result = db.find_similar(&query, 5);
        assert!(result.is_ok());

        let similar = result.unwrap();
        assert_eq!(similar.len(), 5);

        // Verify vector dimensions are preserved
        for (entry, _) in &similar {
            assert_eq!(entry.vector.len(), dimension);
        }

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }
}

#[cfg(test)]
mod similar_integration_tests {
    use super::*;

    #[test]
    fn test_complete_similarity_workflow() {
        let _config = Config {
            providers: HashMap::new(),
            default_provider: Some("openai".to_string()),
            default_model: Some("text-embedding-3-small".to_string()),
            aliases: HashMap::new(),
            system_prompt: None,
            templates: HashMap::new(),
            max_tokens: None,
            temperature: None,
            stream: None,
        };

        let db_name = format!("similarity_workflow_test_{}", std::process::id());
        let model = "text-embedding-3-small";
        let provider = "openai";

        // 1. Create database and add vectors
        let db = VectorDatabase::new(&db_name).unwrap();

        let test_vectors = vec![
            (
                "Artificial intelligence research",
                vec![0.9, 0.1, 0.0, 0.0, 0.0],
            ),
            ("Machine learning algorithms", vec![0.8, 0.2, 0.0, 0.0, 0.0]),
            (
                "Web development with JavaScript",
                vec![0.0, 0.0, 0.9, 0.1, 0.0],
            ),
            ("Database design principles", vec![0.0, 0.0, 0.1, 0.9, 0.0]),
            ("Cooking Italian cuisine", vec![0.0, 0.0, 0.0, 0.0, 1.0]),
        ];

        for (text, vector) in &test_vectors {
            db.add_vector(text, vector, model, provider).unwrap();
        }

        // 2. Test model resolution from database
        let model_info = db.get_model_info().unwrap().unwrap();
        assert_eq!(model_info.0, model);
        assert_eq!(model_info.1, provider);

        // 3. Test similarity search with AI-related query
        let ai_query = vec![0.85, 0.15, 0.0, 0.0, 0.0];
        let similar = db.find_similar(&ai_query, 3).unwrap();

        assert_eq!(similar.len(), 3);

        // Top results should be AI-related
        assert!(
            similar[0].0.text.to_lowercase().contains("artificial")
                || similar[0].0.text.to_lowercase().contains("intelligence")
        );
        assert!(
            similar[1].0.text.to_lowercase().contains("machine")
                || similar[1].0.text.to_lowercase().contains("learning")
        );

        // 4. Test similarity search with different query
        let web_query = vec![0.0, 0.0, 0.8, 0.2, 0.0];
        let web_similar = db.find_similar(&web_query, 2).unwrap();

        assert_eq!(web_similar.len(), 2);
        assert!(
            web_similar[0].0.text.to_lowercase().contains("web")
                || web_similar[0].0.text.to_lowercase().contains("javascript")
        );

        // 5. Test with limit larger than available vectors
        let all_similar = db.find_similar(&ai_query, 10).unwrap();
        assert_eq!(all_similar.len(), 5); // Should return all 5 vectors

        // 6. Verify similarity ordering
        for i in 1..all_similar.len() {
            assert!(all_similar[i - 1].1 >= all_similar[i].1);
        }

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }

    #[test]
    fn test_similarity_with_model_consistency() {
        let db_name = format!("model_consistency_test_{}", std::process::id());
        let db = VectorDatabase::new(&db_name).unwrap();

        // Add vectors with consistent model
        let model = "text-embedding-3-small";
        let provider = "openai";

        db.add_vector("First text", &vec![0.1, 0.2, 0.3], model, provider)
            .unwrap();
        db.add_vector("Second text", &vec![0.2, 0.3, 0.4], model, provider)
            .unwrap();

        // Verify model consistency
        let model_info = db.get_model_info().unwrap().unwrap();
        assert_eq!(model_info.0, model);
        assert_eq!(model_info.1, provider);

        // Search should work with consistent dimensions
        let query = vec![0.15, 0.25, 0.35];
        let similar = db.find_similar(&query, 2).unwrap();
        assert_eq!(similar.len(), 2);

        // Both results should have the same model info
        for (entry, _) in &similar {
            assert_eq!(entry.model, model);
            assert_eq!(entry.provider, provider);
        }

        // Cleanup
        VectorDatabase::delete_database(&db_name).unwrap();
    }
}