Files
semantic-kernel/python/tests/samples/test_concepts.py
Eduard van Valkenburg 003cc4c010 Python: Feature python vector stores preb (#12271)
### Motivation and Context

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->
Overhaul of the VectorStores to get it matching with dotnet and the
latest development there.

Closes: 
- #11938 
- #11598 
- #11597
- #11517 
- #10561 
- #10391 
- #9911 
- #9892 
- #10867 

### Description

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

Many changes, some highlights:
- Moved from different vector fields types, to a single with a enum
first VectorStoreField()
- Renamed several items
- Made embeddinggenerrator a part of the vector field spec and do
automatic vectorization from there or from the same param on a
collection
- Adds hybrid search using the same setup
- Removed the intermediate TextSeachVectorSearch object need, by
allowing `create_search_function` directly on a collection

Will write out full changes in a migration guide in the docs.

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [ ] I didn't break anyone 😄
2025-06-19 18:34:59 +00:00

387 lines
15 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
import copy
import os
from collections.abc import Awaitable, Callable
from typing import Any
import pytest
from pytest import mark, param
from samples.concepts.auto_function_calling.chat_completion_with_auto_function_calling import (
main as chat_completion_with_function_calling,
)
from samples.concepts.auto_function_calling.functions_defined_in_json_prompt import (
main as function_defined_in_json_prompt,
)
from samples.concepts.auto_function_calling.functions_defined_in_yaml_prompt import (
main as function_defined_in_yaml_prompt,
)
from samples.concepts.caching.semantic_caching import main as semantic_caching
from samples.concepts.chat_completion.simple_chatbot import main as simple_chatbot
from samples.concepts.chat_completion.simple_chatbot_kernel_function import main as simple_chatbot_kernel_function
from samples.concepts.chat_completion.simple_chatbot_logit_bias import main as simple_chatbot_logit_bias
from samples.concepts.chat_completion.simple_chatbot_streaming import main as simple_chatbot_streaming
from samples.concepts.chat_completion.simple_chatbot_with_image import main as simple_chatbot_with_image
from samples.concepts.embedding.text_embedding_generation import main as text_embedding_generation
from samples.concepts.filtering.auto_function_invoke_filters import main as auto_function_invoke_filters
from samples.concepts.filtering.function_invocation_filters import main as function_invocation_filters
from samples.concepts.filtering.function_invocation_filters_stream import main as function_invocation_filters_stream
from samples.concepts.filtering.prompt_filters import main as prompt_filters
from samples.concepts.filtering.retry_with_different_model import main as retry_with_different_model
from samples.concepts.functions.kernel_arguments import main as kernel_arguments
from samples.concepts.grounding.grounded import main as grounded
from samples.concepts.images.image_generation import main as image_generation
from samples.concepts.local_models.lm_studio_chat_completion import main as lm_studio_chat_completion
from samples.concepts.local_models.lm_studio_text_embedding import main as lm_studio_text_embedding
from samples.concepts.local_models.ollama_chat_completion import main as ollama_chat_completion
from samples.concepts.mcp.agent_with_mcp_agent import main as agent_with_mcp_agent
from samples.concepts.memory.simple_memory import main as simple_memory
from samples.concepts.plugins.openai_function_calling_with_custom_plugin import (
main as openai_function_calling_with_custom_plugin,
)
from samples.concepts.plugins.plugins_from_dir import main as plugins_from_dir
from samples.concepts.prompt_templates.azure_chat_gpt_api_handlebars import main as azure_chat_gpt_api_handlebars
from samples.concepts.prompt_templates.azure_chat_gpt_api_jinja2 import main as azure_chat_gpt_api_jinja2
from samples.concepts.prompt_templates.configuring_prompts import main as configuring_prompts
from samples.concepts.prompt_templates.load_yaml_prompt import main as load_yaml_prompt
from samples.concepts.prompt_templates.template_language import main as template_language
from samples.concepts.rag.rag_with_vector_collection import main as rag_with_vector_collection
from samples.concepts.service_selector.custom_service_selector import main as custom_service_selector
from samples.concepts.text_completion.text_completion import main as text_completion
from samples.getting_started_with_agents.chat_completion.step01_chat_completion_agent_simple import (
main as step1_chat_completion_agent_simple,
)
from samples.getting_started_with_agents.chat_completion.step03_chat_completion_agent_with_kernel import (
main as step2_chat_completion_agent_with_kernel,
)
from samples.getting_started_with_agents.chat_completion.step04_chat_completion_agent_plugin_simple import (
main as step3_chat_completion_agent_plugin_simple,
)
from samples.getting_started_with_agents.chat_completion.step05_chat_completion_agent_plugin_with_kernel import (
main as step4_chat_completion_agent_plugin_with_kernel,
)
from samples.getting_started_with_agents.chat_completion.step06_chat_completion_agent_group_chat import (
main as step5_chat_completion_agent_group_chat,
)
from samples.getting_started_with_agents.openai_assistant.step1_assistant import main as step1_openai_assistant
from tests.utils import retry
# These environment variable names are used to control which samples are run during integration testing.
# This has to do with the setup of the tests and the services they depend on.
COMPLETIONS_CONCEPT_SAMPLE = "COMPLETIONS_CONCEPT_SAMPLE"
MEMORY_CONCEPT_SAMPLE = "MEMORY_CONCEPT_SAMPLE"
concepts = [
param(
semantic_caching,
[],
id="semantic_caching",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
simple_chatbot,
["Why is the sky blue in one sentence?", "exit"],
id="simple_chatbot",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
simple_chatbot_streaming,
["Why is the sky blue in one sentence?", "exit"],
id="simple_chatbot_streaming",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
simple_chatbot_with_image,
["exit"],
id="simple_chatbot_with_image",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
simple_chatbot_logit_bias,
["Who has the most career points in NBA history?", "exit"],
id="simple_chatbot_logit_bias",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
simple_chatbot_kernel_function,
["Why is the sky blue in one sentence?", "exit"],
id="simple_chatbot_kernel_function",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
chat_completion_with_function_calling,
["What is 3+3?", "exit"],
id="chat_completion_with_function_calling",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
auto_function_invoke_filters,
["What is 3+3?", "exit"],
id="auto_function_invoke_filters",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
function_invocation_filters,
["What is 3+3?", "exit"],
id="function_invocation_filters",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
function_invocation_filters_stream,
["What is 3+3?", "exit"],
id="function_invocation_filters_stream",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
prompt_filters,
["What is the fastest animal?", "exit"],
id="prompt_filters",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
retry_with_different_model,
[],
id="retry_with_different_model",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None,
reason="Not running completion samples.",
),
),
param(
kernel_arguments,
[],
id="kernel_arguments",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
grounded,
[],
id="grounded",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
openai_function_calling_with_custom_plugin,
[],
id="openai_function_calling_with_custom_plugin",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
plugins_from_dir,
[],
id="plugins_from_dir",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
azure_chat_gpt_api_handlebars,
["What is 3+3?", "exit"],
id="azure_chat_gpt_api_handlebars",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
azure_chat_gpt_api_jinja2,
["What is 3+3?", "exit"],
id="azure_chat_gpt_api_jinja2",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
agent_with_mcp_agent,
["what restaurants can I choose from?", "the farm sounds nice, what are the specials there?", "exit"],
id="agent_with_mcp_agent",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
configuring_prompts,
["What is my name?", "exit"],
id="configuring_prompts",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
load_yaml_prompt,
[],
id="load_yaml_prompt",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
template_language,
[],
id="template_language",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
simple_memory,
[],
id="simple_memory",
marks=pytest.mark.skipif(os.getenv(MEMORY_CONCEPT_SAMPLE, None) is None, reason="Not running memory samples."),
),
param(rag_with_vector_collection, [], id="rag_with_vector_collection"),
param(
custom_service_selector,
[],
id="custom_service_selector",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
function_defined_in_json_prompt,
["What is 3+3?", "exit"],
id="function_defined_in_json_prompt",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
function_defined_in_yaml_prompt,
["What is 3+3?", "exit"],
id="function_defined_in_yaml_prompt",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
step1_chat_completion_agent_simple,
[],
id="step1_chat_completion_agent_simple",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
step2_chat_completion_agent_with_kernel,
[],
id="step2_chat_completion_agent_with_kernel",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
step3_chat_completion_agent_plugin_simple,
[],
id="step3_chat_completion_agent_plugin_simple",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
step4_chat_completion_agent_plugin_with_kernel,
[],
id="step4_chat_completion_agent_plugin_with_kernel",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
step5_chat_completion_agent_group_chat,
[],
id="step5_chat_completion_agent_group_chat",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
step1_openai_assistant,
[],
id="step1_openai_assistant",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
ollama_chat_completion,
["Why is the sky blue?", "exit"],
id="ollama_chat_completion",
marks=pytest.mark.skip(reason="Need to set up Ollama locally. Check out the module for more details."),
),
param(
lm_studio_chat_completion,
["Why is the sky blue?", "exit"],
id="lm_studio_chat_completion",
marks=pytest.mark.skip(reason="Need to set up LM Studio locally. Check out the module for more details."),
),
param(
lm_studio_text_embedding,
[],
id="lm_studio_text_embedding",
marks=pytest.mark.skip(reason="Need to set up LM Studio locally. Check out the module for more details."),
),
param(
image_generation,
[],
id="image_generation",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
text_completion,
[],
id="text_completion",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
param(
text_embedding_generation,
[],
id="text_embedding_generation",
marks=pytest.mark.skipif(
os.getenv(COMPLETIONS_CONCEPT_SAMPLE, None) is None, reason="Not running completion samples."
),
),
]
@mark.parametrize("sample, responses", concepts)
async def test_concepts(sample: Callable[..., Awaitable[Any]], responses: list[str], monkeypatch):
saved_responses = copy.deepcopy(responses)
def reset():
responses.clear()
responses.extend(saved_responses)
monkeypatch.setattr("builtins.input", lambda _: responses.pop(0))
await retry(sample, retries=3, reset=reset)