Skip to content

Conversation

@KSemenenko
Copy link
Member

Summary

  • upgrade to Microsoft.Extensions.AI 9.5.0 and enable central package management
  • update Together Microsoft.Extensions.AI adapters and related clients to align with the latest abstractions and add evaluation warning suppression
  • refresh unit tests and helpers for the new adapters and REST clients

Testing

  • /root/.dotnet/dotnet build Together.slnx
  • /root/.dotnet/dotnet test Together.slnx (fails: requires live API access / valid credentials)

https://chatgpt.com/codex/tasks/task_e_68fc981b30188326bbe15abdb9fcb04d

Copilot AI review requested due to automatic review settings October 25, 2025 11:24
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR upgrades the Together.NET SDK to use Microsoft.Extensions.AI 9.5.0, enables central package management, and adds comprehensive support for new Together API resources. The changes expand API coverage to match reference Python/TypeScript SDKs while maintaining compatibility with the latest Microsoft.Extensions.AI abstractions.

Key changes:

  • Upgraded Microsoft.Extensions.AI to version 9.5.0 and enabled centralized package version management
  • Added support for 8 new Together API resources (Audio, Batch, Endpoints, Hardware, Evaluation, CodeInterpreter, Jobs, Videos)
  • Updated Microsoft.Extensions.AI adapters (chat, embeddings, speech-to-text, image generation) to align with latest abstractions

Reviewed Changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
docs/api-coverage.md Documents complete API coverage including new endpoints and Microsoft.Extensions.AI adapters
Together/TogetherClient.cs Adds properties for 8 new client instances (Audio, Batch, Endpoints, etc.)
Together/Together.csproj Enables central package management and suppresses MEAI001 warning
Together/Models/Videos/VideoModels.cs Defines models for video generation API
Together/Models/Jobs/JobModels.cs Defines models for background job status tracking
Together/Models/Evaluations/EvaluationModels.cs Defines models for evaluation workflows
Together/Models/Endpoints/EndpointsModels.cs Defines models for dedicated endpoint management
Together/Models/CodeInterpreter/CodeInterpreterModels.cs Defines models for code execution API
Together/Models/ChatCompletions/FunctionTool.cs Updates Parameters property to accept nullable values
Together/Models/ChatCompletions/ChatCompletionRequest.cs Adds AdditionalParameters extension data
Together/Models/Batch/BatchJob.cs Defines models for batch processing jobs
Together/Models/Audio/AudioModels.cs Defines models for speech synthesis and transcription
Together/Extensions/MicrosoftAI/TogetherAISpeechToTextClient.cs Implements ISpeechToTextClient for audio transcription
Together/Extensions/MicrosoftAI/TogetherAIImageClient.cs Implements IChatClient for image generation
Together/Extensions/MicrosoftAI/TogetherAIEmbeddingGenerator.cs Implements IEmbeddingGenerator for text embeddings
Together/Extensions/MicrosoftAI/TogetherAIClientExtensions.cs Provides extension methods for creating Microsoft.AI adapters
Together/Extensions/MicrosoftAI/TogetherAIChatClient.cs Implements IChatClient for chat completions
Together/Extensions/MicrosoftAI/ChatOptionConverters.cs Converts between Microsoft.Extensions.AI and Together SDK types
Together/Clients/*.cs Implements REST clients for Video, Job, Hardware, Evaluation, Endpoint, CodeInterpreter, Batch, and Audio APIs
Together.slnx New XML-based solution file
Together.sln Removed old solution file
Together.Tests/Together.Tests.csproj Updates test project to use central package management
Together.Tests/MicrosoftAI/MicrosoftAIAdapterTests.cs Tests for Microsoft.Extensions.AI adapter implementations
Together.Tests/Clients/NewResourceClientTests.cs Tests for new REST client implementations
Together.SemanticKernel/Together.SemanticKernel.csproj Removes hardcoded package version
Directory.Packages.props Centralized package version definitions
Directory.Build.props Enables central package management
.github/workflows/*.yml Updates workflows to use Together.slnx

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

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

The MEAI001 warning suppression is not documented. Add a comment explaining why this warning is suppressed (e.g., '').

Suggested change
<IsTestProject>true</IsTestProject>
<IsTestProject>true</IsTestProject>
<!-- MEAI001: Suppresses experimental API warnings for Microsoft.Extensions.AI 9.5.0 preview features -->

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +40
var request = new AudioFileRequest
{
Content = audio,
FileName = options?.AdditionalProperties?.TryGetValue("file_name", out var name) == true ? name?.ToString() ?? "audio.wav" : "audio.wav",
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

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

The nested ternary operation makes this line difficult to read. Consider extracting the file name resolution logic into a separate helper method or local variable.

Suggested change
var request = new AudioFileRequest
{
Content = audio,
FileName = options?.AdditionalProperties?.TryGetValue("file_name", out var name) == true ? name?.ToString() ?? "audio.wav" : "audio.wav",
var fileName = "audio.wav";
if (options?.AdditionalProperties?.TryGetValue("file_name", out var name) == true)
{
fileName = name?.ToString() ?? "audio.wav";
}
var request = new AudioFileRequest
{
Content = audio,
FileName = fileName,

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +88
Height = options?.AdditionalProperties?.TryGetValue("height", out var height) == true && int.TryParse(height?.ToString(), out var h) ? h : (int?)null,
Width = options?.AdditionalProperties?.TryGetValue("width", out var width) == true && int.TryParse(width?.ToString(), out var w) ? w : (int?)null,
Steps = options?.AdditionalProperties?.TryGetValue("steps", out var steps) == true && int.TryParse(steps?.ToString(), out var s) ? s : (int?)null,
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

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

Lines 86-88 contain duplicate parsing logic. Extract this pattern into a reusable helper method like TryGetIntProperty(Dictionary<string, object?> properties, string key).

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +15
return await SendRequestAsync<CreateVideoRequest, CreateVideoResponse>("/../v2/videos", request, cancellationToken);
}

public async Task<VideoJob> RetrieveAsync(string id, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<VideoJob>($"/../v2/videos/{id}", HttpMethod.Get, null, cancellationToken);
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

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

The path prefix /../v2/videos is unusual and potentially error-prone. Verify that the /../ prefix is intentional or if the path should be /v2/videos instead.

Suggested change
return await SendRequestAsync<CreateVideoRequest, CreateVideoResponse>("/../v2/videos", request, cancellationToken);
}
public async Task<VideoJob> RetrieveAsync(string id, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<VideoJob>($"/../v2/videos/{id}", HttpMethod.Get, null, cancellationToken);
return await SendRequestAsync<CreateVideoRequest, CreateVideoResponse>("/v2/videos", request, cancellationToken);
}
public async Task<VideoJob> RetrieveAsync(string id, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<VideoJob>($"/v2/videos/{id}", HttpMethod.Get, null, cancellationToken);

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +15
return await SendRequestAsync<CreateVideoRequest, CreateVideoResponse>("/../v2/videos", request, cancellationToken);
}

public async Task<VideoJob> RetrieveAsync(string id, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<VideoJob>($"/../v2/videos/{id}", HttpMethod.Get, null, cancellationToken);
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

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

The path prefix /../v2/videos is unusual and potentially error-prone. Verify that the /../ prefix is intentional or if the path should be /v2/videos instead.

Suggested change
return await SendRequestAsync<CreateVideoRequest, CreateVideoResponse>("/../v2/videos", request, cancellationToken);
}
public async Task<VideoJob> RetrieveAsync(string id, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<VideoJob>($"/../v2/videos/{id}", HttpMethod.Get, null, cancellationToken);
return await SendRequestAsync<CreateVideoRequest, CreateVideoResponse>("/v2/videos", request, cancellationToken);
}
public async Task<VideoJob> RetrieveAsync(string id, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<VideoJob>($"/v2/videos/{id}", HttpMethod.Get, null, cancellationToken);

Copilot uses AI. Check for mistakes.
PresencePenalty = options?.PresencePenalty,
FrequencyPenalty = options?.FrequencyPenalty,
MaxTokens = options?.MaxOutputTokens,
Seed = options?.Seed is long seedValue ? (ulong?)seedValue : null,
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

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

Casting a signed long to ulong can produce incorrect results for negative values. Either add validation to reject negative seeds or document that negative values will be converted incorrectly.

Suggested change
Seed = options?.Seed is long seedValue ? (ulong?)seedValue : null,
Seed = options?.Seed is long seedValue && seedValue >= 0 ? (ulong?)seedValue : null,

Copilot uses AI. Check for mistakes.
continue;
}

var payload = line.Substring("data:".Length).Trim();
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

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

Using Substring can throw if the line is shorter than the prefix. Use line["data:".Length..] or add a length check before calling Substring to prevent potential exceptions.

Copilot uses AI. Check for mistakes.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +124 to +147
private static AudioTranscriptionResult BuildTranscriptionResult(string responseFormat, string json)
{
var result = new AudioTranscriptionResult { RawJson = json };
var format = responseFormat?.ToLowerInvariant();
result.VerboseResponse = format == "verbose_json"
? JsonSerializer.Deserialize<AudioTranscriptionVerboseResponse>(json)
: null;
result.Response = format == "verbose_json"
? null
: JsonSerializer.Deserialize<AudioTranscriptionResponse>(json);

return result;
}

private static AudioTranslationResult BuildTranslationResult(string responseFormat, string json)
{
var result = new AudioTranslationResult { RawJson = json };
var format = responseFormat?.ToLowerInvariant();
result.VerboseResponse = format == "verbose_json"
? JsonSerializer.Deserialize<AudioTranslationVerboseResponse>(json)
: null;
result.Response = format == "verbose_json"
? null
: JsonSerializer.Deserialize<AudioTranslationResponse>(json);

Choose a reason for hiding this comment

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

P1 Badge Handle non‑JSON transcription formats without deserializing

Both BuildTranscriptionResult and BuildTranslationResult always run the payload through JsonSerializer.Deserialize whenever the responseFormat isn’t verbose_json. The audio APIs also return plain text (e.g. "text", "srt", "vtt") when those formats are requested, so calling this code with any non‑JSON format will throw a JsonException even though the HTTP call succeeded. Consider returning the raw text (or leaving Response/VerboseResponse null) when the format isn’t JSON instead of deserializing unconditionally.

Useful? React with 👍 / 👎.

@KSemenenko KSemenenko merged commit 420c126 into main Oct 25, 2025
1 check passed
@KSemenenko KSemenenko deleted the codex/audit-apis-and-migrate-to-dotnet-solution branch October 25, 2025 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants