Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions dotnet/docs/OPENAI-CONNECTOR-MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ We have the two only specific cases where we attempted to auto-correct the endpo
+ http://any-host-and-port/v1
```

### 5.1 OpenAI-Compatible Endpoints Example (e.g., PZERO)

When connecting to third-party OpenAI-compatible endpoints such as PZERO (`https://api.pzero.studio/v1`), provide the `/v1` root endpoint, API key, and model ID:

```csharp
using Microsoft.SemanticKernel;

var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(
modelId: "deepseek-v4-flash",
endpoint: new Uri("https://api.pzero.studio/v1"),
apiKey: Environment.GetEnvironmentVariable("PZERO_API_KEY")!
);
var kernel = builder.Build();
```

## 6. SemanticKernel MetaPackage

To be retro compatible with the new OpenAI and AzureOpenAI Connectors, our `Microsoft.SemanticKernel` meta package changed its dependency to use the new `Microsoft.SemanticKernel.Connectors.AzureOpenAI` package that depends on the `Microsoft.SemanticKernel.Connectors.OpenAI` package. This way if you are using the metapackage, no change is needed to get access to `Azure` related types.
Expand Down Expand Up @@ -181,15 +197,14 @@ The type also changed from `CompletionsUsage` to `ChatTokenUsage`.
```diff
- Before
- var usage = FunctionResult.Metadata?["Usage"] as CompletionsUsage;
- var completionTokesn = usage?.CompletionTokens ?? 0;
- var completionTokens = usage?.CompletionTokens ?? 0;
- var promptTokens = usage?.PromptTokens ?? 0;

+ After
+ var usage = FunctionResult.Metadata?["Usage"] as ChatTokenUsage;
+ var promptTokens = usage?.InputTokens ?? 0;
+ var completionTokens = completionTokens: usage?.OutputTokens ?? 0;

totalTokens: usage?.TotalTokens ?? 0;
+ var completionTokens = usage?.OutputTokens ?? 0;
+ var totalTokens = usage?.TotalTokens ?? 0;
```

#### 9.9 OpenAIClient
Expand Down
92 changes: 92 additions & 0 deletions dotnet/samples/Concepts/ChatCompletion/PZero_ChatCompletion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

namespace ChatCompletion;

/// <summary>
/// This example shows how to use the OpenAI connector with PZERO's OpenAI-compatible endpoint.
/// <list type="number">
/// <item>Get a key from https://pzero.studio/agents</item>
/// <item>Set the environment variable PZERO_API_KEY with your key</item>
/// <item>Configure the endpoint to https://api.pzero.studio/v1</item>
/// <item>Run the example</item>
/// </list>
/// </summary>
public class PZero_ChatCompletion(ITestOutputHelper output) : BaseTest(output)
{
/// <summary>
/// This example shows how to configure PZERO OpenAI-compatible endpoint with Kernel InvokeAsync.
/// </summary>
[Fact]
public async Task UsingKernelWithPZero()
{
Console.WriteLine($"======== PZERO - Chat Completion - {nameof(UsingKernelWithPZero)} ========");

var apiKey = Environment.GetEnvironmentVariable("PZERO_API_KEY");
if (string.IsNullOrEmpty(apiKey))
{
Console.WriteLine("PZERO_API_KEY environment variable is not set. Skipping execution.");
return;
}

var modelId = "deepseek-v4-flash";
var endpoint = new Uri("https://api.pzero.studio/v1");

var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: modelId,
endpoint: endpoint,
apiKey: apiKey)
.Build();

var prompt = @"Rewrite the text between triple backticks into a business email. Use a professional tone, be clear and concise.
Sign the email as AI Assistant.

Text: ```{{$input}}```";

var mailFunction = kernel.CreateFunctionFromPrompt(prompt, new OpenAIPromptExecutionSettings
{
TopP = 0.5,
MaxTokens = 1000,
});

var response = await kernel.InvokeAsync(mailFunction, new() { ["input"] = "Tell David that I will complete the report by Friday." });
Console.WriteLine(response);
}

/// <summary>
/// Sample showing how to use <see cref="IChatCompletionService"/> directly with a <see cref="ChatHistory"/> against PZERO.
/// </summary>
[Fact]
public async Task UsingServiceNonStreamingWithPZero()
{
Console.WriteLine($"======== PZERO - Chat Completion - {nameof(UsingServiceNonStreamingWithPZero)} ========");

var apiKey = Environment.GetEnvironmentVariable("PZERO_API_KEY");
if (string.IsNullOrEmpty(apiKey))
{
Console.WriteLine("PZERO_API_KEY environment variable is not set. Skipping execution.");
return;
}

var modelId = "deepseek-v4-flash";
var endpoint = new Uri("https://api.pzero.studio/v1");

OpenAIChatCompletionService chatService = new(modelId: modelId, endpoint: endpoint, apiKey: apiKey);

Console.WriteLine("Chat content:");
Console.WriteLine("------------------------");

var chatHistory = new ChatHistory("You are a helpful assistant.");

chatHistory.AddUserMessage("Hello! Can you summarize the purpose of Semantic Kernel in one sentence?");
this.OutputLastMessage(chatHistory);

var reply = await chatService.GetChatMessageContentAsync(chatHistory);
chatHistory.Add(reply);
this.OutputLastMessage(chatHistory);
}
}
1 change: 1 addition & 0 deletions dotnet/samples/Concepts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ dotnet test -l "console;verbosity=detailed" --filter "FullyQualifiedName=ChatCom
- [OpenAI_RepeatedFunctionCalling](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/ChatCompletion/OpenAI_RepeatedFunctionCalling.cs)
- [OpenAI_StructuredOutputs](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/ChatCompletion/OpenAI_StructuredOutputs.cs)
- [OpenAI_UsingLogitBias](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/ChatCompletion/OpenAI_UsingLogitBias.cs)
- [PZero_ChatCompletion](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/ChatCompletion/PZero_ChatCompletion.cs)

### DependencyInjection - Examples on using `DI Container`

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright (c) Microsoft. All rights reserved.

import asyncio
import os

from openai import AsyncOpenAI

from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.kernel import Kernel

# This concept sample shows how to use the OpenAI connector with PZERO's
# OpenAI-compatible endpoint: https://api.pzero.studio/v1
# Get an API key from https://pzero.studio/agents and set PZERO_API_KEY.

system_message = """
You are a helpful and concise AI assistant.
"""

kernel = Kernel()

service_id = "pzero-deepseek"

api_key = os.environ.get("PZERO_API_KEY", "your-pzero-api-key")
endpoint = "https://api.pzero.studio/v1"
model_id = "deepseek-v4-flash"

open_ai_client: AsyncOpenAI = AsyncOpenAI(
api_key=api_key,
base_url=endpoint,
)
kernel.add_service(OpenAIChatCompletion(service_id=service_id, ai_model_id=model_id, async_client=open_ai_client))

settings = kernel.get_prompt_execution_settings_from_service_id(service_id)
settings.max_tokens = 1000
settings.temperature = 0.7

chat_function = kernel.add_function(
plugin_name="ChatBot",
function_name="Chat",
prompt="{{$chat_history}}{{$user_input}}",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sample cannot run as written. The function is registered with a {{$chat_history}} template but without enabling dangerous content, and kernel.invoke is later called with a ChatHistory object bound to chat_history. During prompt rendering, complex (non-string) argument values are rejected unless dangerous content is allowed, so the invoke raises NotImplementedError: Argument 'chat_history' has a value that doesn't support automatic encoding... before any request reaches the endpoint. py_compile does not exercise this path, so it passes while the sample fails on every run. Register the template the same way the working simple_chatbot_kernel_function.py sample does — via a PromptTemplateConfig(template="{{$chat_history}}{{$user_input}}", template_format="semantic-kernel", allow_dangerously_set_content=True) passed as prompt_template_config — so the trusted chat history renders and the sample can complete a call.

template_format="semantic-kernel",
prompt_execution_settings=settings,
)


async def main() -> None:
chat_history = ChatHistory(system_message=system_message)
user_message = "What is Semantic Kernel in one sentence?"
chat_history.add_user_message(user_message)

answer = await kernel.invoke(chat_function, KernelArguments(user_input=user_message, chat_history=chat_history))
print(f"User:> {user_message}")
print(f"Assistant:> {answer}")


if __name__ == "__main__":
asyncio.run(main())
Loading