Skip to content

Commit 3ebe54b

Browse files
committed
Fix Microsoft AI adapters for latest abstractions
1 parent a56426a commit 3ebe54b

37 files changed

Lines changed: 2123 additions & 51 deletions

.github/workflows/dotnet.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ jobs:
2525

2626
# run build and test
2727
- name: Restore dependencies
28-
run: dotnet restore
28+
run: dotnet restore Together.slnx
2929

3030
- name: Build
31-
run: dotnet build --no-restore --configuration Release
31+
run: dotnet build Together.slnx --no-restore --configuration Release
3232

3333
- name: Test and Collect Code Coverage
34-
run: dotnet test --configuration Release -p:CollectCoverage=true -p:CoverletOutput=coverage/
34+
run: dotnet test Together.slnx --no-build --configuration Release -p:CollectCoverage=true -p:CoverletOutput=coverage/
3535

3636
- name: Copy coverage files
3737
run: |
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Monitor upstream SDKs
2+
3+
on:
4+
schedule:
5+
- cron: '0 6 * * *'
6+
workflow_dispatch:
7+
8+
jobs:
9+
check-updates:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
issues: write
13+
contents: read
14+
15+
steps:
16+
- name: Check upstream repositories
17+
id: check
18+
uses: actions/github-script@v7
19+
with:
20+
script: |
21+
const repos = [
22+
{ owner: 'togethercomputer', repo: 'together-python' },
23+
{ owner: 'togethercomputer', repo: 'together-typescript' }
24+
];
25+
const cutoff = new Date(Date.now() - 24 * 60 * 60 * 1000);
26+
const results = [];
27+
for (const target of repos) {
28+
const { data } = await github.repos.listCommits({ owner: target.owner, repo: target.repo, per_page: 10 });
29+
const recent = data.filter(c => new Date(c.commit.committer.date) > cutoff);
30+
if (recent.length > 0) {
31+
results.push({
32+
owner: target.owner,
33+
repo: target.repo,
34+
commits: recent.map(c => ({
35+
sha: c.sha.substring(0, 7),
36+
message: c.commit.message.split('\n')[0],
37+
url: c.html_url,
38+
date: c.commit.committer.date
39+
}))
40+
});
41+
}
42+
}
43+
core.setOutput('updates', JSON.stringify(results));
44+
core.info(`Found ${results.length} repositories with updates.`);
45+
46+
- name: Create tracking issue
47+
if: steps.check.outputs.updates != '[]'
48+
uses: actions/github-script@v7
49+
env:
50+
UPDATES: ${{ steps.check.outputs.updates }}
51+
with:
52+
script: |
53+
const updates = JSON.parse(process.env.UPDATES ?? '[]');
54+
const today = new Date().toISOString().split('T')[0];
55+
let body = 'Detected new upstream commits:\n\n';
56+
for (const repo of updates) {
57+
body += `### ${repo.owner}/${repo.repo}\n`;
58+
for (const commit of repo.commits) {
59+
body += `- [${commit.sha}](${commit.url}) ${commit.message} (_${commit.date}_)\n`;
60+
}
61+
body += '\n';
62+
}
63+
await github.issues.create({
64+
owner: context.repo.owner,
65+
repo: context.repo.repo,
66+
title: `Upstream SDK updates ${today}`,
67+
body,
68+
labels: ['upstream-monitor']
69+
});
70+
71+
- name: No updates found
72+
if: steps.check.outputs.updates == '[]'
73+
run: echo "No upstream changes detected in the last 24 hours."

.github/workflows/nuget.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ name: nuget
22

33
on:
44
push:
5-
branches: [ main ]
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
68

79
jobs:
810
nuget-pack:
@@ -18,16 +20,16 @@ jobs:
1820
dotnet-version: '9.0.x'
1921

2022
- name: Restore dependencies
21-
run: dotnet restore
23+
run: dotnet restore Together.slnx
2224

2325
- name: Build
24-
run: dotnet build --configuration Release
26+
run: dotnet build Together.slnx --configuration Release --no-restore
2527

2628
- name: Test
27-
run: dotnet test --configuration Release
29+
run: dotnet test Together.slnx --configuration Release --no-build
2830

2931
- name: Pack
30-
run: dotnet pack --configuration Release -p:IncludeSymbols=false -p:SymbolPackageFormat=snupkg -o "packages"
32+
run: dotnet pack Together.slnx --configuration Release --no-build -p:IncludeSymbols=false -p:SymbolPackageFormat=snupkg -o "packages"
3133

3234
- name: Push
3335
run: dotnet nuget push "packages/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate

Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<LangVersion>13</LangVersion>
66
<EnableNETAnalyzers>true</EnableNETAnalyzers>
77
<Nullable>enable</Nullable>
8+
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
89
</PropertyGroup>
910

1011
<!--NuGet-->

Directory.Packages.props

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project>
2+
<ItemGroup>
3+
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="9.5.0" />
4+
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.38.0" />
5+
<PackageVersion Include="Microsoft.SemanticKernel.Core" Version="1.38.0" />
6+
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
7+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
8+
<PackageVersion Include="Shouldly" Version="4.3.0" />
9+
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
10+
<PackageVersion Include="xunit" Version="2.9.3" />
11+
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
12+
<PackageVersion Include="Moq" Version="4.20.72" />
13+
</ItemGroup>
14+
</Project>

Together.SemanticKernel/Together.SemanticKernel.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</ItemGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.38.0" />
12+
<PackageReference Include="Microsoft.SemanticKernel.Core" />
1313
</ItemGroup>
1414

1515

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Net;
4+
using System.Net.Http;
5+
using System.Text;
6+
using Together.Clients;
7+
using Together.Models.Audio;
8+
using Together.Models.Batch;
9+
using Together.Models.CodeInterpreter;
10+
using Together.Models.Evaluations;
11+
using Together.Models.Videos;
12+
13+
namespace Together.Tests.Clients;
14+
15+
public class NewResourceClientTests : TestBase
16+
{
17+
[Fact]
18+
public async Task BatchClient_CreateAsync_ReturnsJob()
19+
{
20+
var response = new HttpResponseMessage
21+
{
22+
StatusCode = HttpStatusCode.OK,
23+
Content = new StringContent(
24+
"""{"job":{"id":"batch_1","input_file_id":"file_1","user_id":"user","file_size_bytes":10,"status":"IN_PROGRESS","job_deadline":"2024-01-01T00:00:00Z","created_at":"2024-01-01T00:00:00Z","endpoint":"/v1/completions","progress":0.5}}""")
25+
};
26+
27+
var client = new BatchClient(CreateMockHttpClient(response));
28+
var result = await client.CreateAsync(new BatchCreateRequest { InputFileId = "file_1", Endpoint = "/v1/completions" });
29+
30+
Assert.Equal("batch_1", result.Id);
31+
Assert.Equal("file_1", result.InputFileId);
32+
}
33+
34+
[Fact]
35+
public async Task EndpointClient_ListAsync_ParsesResponse()
36+
{
37+
var json = """{"data":[{"id":"ep1","object":"endpoint","name":"ep","model":"model","type":"dedicated","owner":"user","state":"STARTED","created_at":"2024-01-01T00:00:00Z"}]}""";
38+
var response = new HttpResponseMessage
39+
{
40+
StatusCode = HttpStatusCode.OK,
41+
Content = new StringContent(json)
42+
};
43+
44+
var client = new EndpointClient(CreateMockHttpClient(response));
45+
var results = await client.ListAsync();
46+
47+
Assert.Single(results);
48+
Assert.Equal("ep1", results[0].Id);
49+
}
50+
51+
[Fact]
52+
public async Task HardwareClient_ListAsync_ReturnsHardware()
53+
{
54+
var json = """{"object":"list","data":[{"object":"hardware","id":"hw1","pricing":{"cents_per_minute":1},"specs":{"gpu_type":"A100","gpu_link":"NVLINK","gpu_memory":80,"gpu_count":1},"updated_at":"2024-01-01T00:00:00Z"}]}""";
55+
var response = new HttpResponseMessage
56+
{
57+
StatusCode = HttpStatusCode.OK,
58+
Content = new StringContent(json)
59+
};
60+
61+
var client = new HardwareClient(CreateMockHttpClient(response));
62+
var result = await client.ListAsync();
63+
64+
Assert.Single(result.Data);
65+
Assert.Equal("hw1", result.Data[0].Id);
66+
}
67+
68+
[Fact]
69+
public async Task JobClient_RetrieveAsync_ReturnsJob()
70+
{
71+
var json = """{"job_id":"job-1","args":{},"created_at":"2024-01-01","status":"Queued","status_updates":[],"type":"train","updated_at":"2024-01-01"}""";
72+
var response = new HttpResponseMessage
73+
{
74+
StatusCode = HttpStatusCode.OK,
75+
Content = new StringContent(json)
76+
};
77+
78+
var client = new JobClient(CreateMockHttpClient(response));
79+
var job = await client.RetrieveAsync("job-1");
80+
81+
Assert.Equal("job-1", job.JobId);
82+
Assert.Equal("Queued", job.Status);
83+
}
84+
85+
[Fact]
86+
public async Task EvaluationClient_CreateAsync_ReturnsWorkflow()
87+
{
88+
var json = """{"workflow_id":"wf_1","status":"queued"}""";
89+
var response = new HttpResponseMessage
90+
{
91+
StatusCode = HttpStatusCode.OK,
92+
Content = new StringContent(json)
93+
};
94+
95+
var client = new EvaluationClient(CreateMockHttpClient(response));
96+
var result = await client.CreateAsync(new EvaluationCreateRequest
97+
{
98+
Type = "classify",
99+
Judge = new JudgeModelConfig { Model = "judge", ModelSource = "serverless", SystemTemplate = "template" },
100+
InputDataFilePath = "file.jsonl",
101+
Labels = new List<string> { "yes" },
102+
PassLabels = new List<string> { "yes" }
103+
});
104+
105+
Assert.Equal("wf_1", result.WorkflowId);
106+
Assert.Equal("queued", result.Status);
107+
}
108+
109+
[Fact]
110+
public async Task CodeInterpreterClient_RunAsync_ReturnsOutputs()
111+
{
112+
var json = """{"data":{"session_id":"sess","status":"completed","outputs":[{"type":"stdout","data":"hello"}]}}""";
113+
var response = new HttpResponseMessage
114+
{
115+
StatusCode = HttpStatusCode.OK,
116+
Content = new StringContent(json)
117+
};
118+
119+
var client = new CodeInterpreterClient(CreateMockHttpClient(response));
120+
var result = await client.RunAsync(new CodeInterpreterRequest { Code = "print('hi')" });
121+
122+
Assert.Equal("sess", result.Data.SessionId);
123+
Assert.Single(result.Data.Outputs);
124+
}
125+
126+
[Fact]
127+
public async Task AudioClient_CreateSpeechAsync_ReturnsBytes()
128+
{
129+
var audioBytes = new byte[] { 1, 2, 3 };
130+
var response = new HttpResponseMessage
131+
{
132+
StatusCode = HttpStatusCode.OK,
133+
Content = new ByteArrayContent(audioBytes)
134+
};
135+
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("audio/wav");
136+
137+
var client = new AudioClient(CreateMockHttpClient(response));
138+
var result = await client.CreateSpeechAsync(new AudioSpeechRequest { Model = "model", Input = "hello" });
139+
140+
Assert.NotNull(result.Data);
141+
Assert.Equal(audioBytes, result.Data);
142+
}
143+
144+
[Fact]
145+
public async Task AudioClient_CreateSpeechAsync_ParsesStream()
146+
{
147+
var payload = "data: {\"b64\":\"AQI=\"}\n" +
148+
"data: [DONE]\n";
149+
var response = new HttpResponseMessage
150+
{
151+
StatusCode = HttpStatusCode.OK,
152+
Content = new StringContent(payload)
153+
};
154+
155+
var client = new AudioClient(CreateMockHttpClient(response));
156+
var request = new AudioSpeechRequest { Model = "model", Input = "hi", Stream = true };
157+
var stream = await client.CreateSpeechAsync(request);
158+
159+
var chunks = new List<byte[]>();
160+
await foreach (var chunk in stream.Stream!)
161+
{
162+
chunks.Add(chunk);
163+
}
164+
165+
Assert.Single(chunks);
166+
Assert.Equal(new byte[] { 1, 2 }, chunks[0]);
167+
}
168+
169+
[Fact]
170+
public async Task AudioClient_CreateTranscriptionAsync_ReturnsText()
171+
{
172+
var response = new HttpResponseMessage
173+
{
174+
StatusCode = HttpStatusCode.OK,
175+
Content = new StringContent("""{"text":"Hello"}""")
176+
};
177+
178+
var request = new AudioFileRequest
179+
{
180+
Content = new MemoryStream(Encoding.UTF8.GetBytes("data")),
181+
FileName = "audio.wav",
182+
Model = "whisper"
183+
};
184+
185+
var client = new AudioClient(CreateMockHttpClient(response));
186+
var result = await client.CreateTranscriptionAsync(request);
187+
188+
Assert.Equal("Hello", result.Response!.Text);
189+
}
190+
191+
[Fact]
192+
public async Task VideoClient_CreateAsync_ReturnsId()
193+
{
194+
var response = new HttpResponseMessage
195+
{
196+
StatusCode = HttpStatusCode.OK,
197+
Content = new StringContent("""{"id":"video_1"}""")
198+
};
199+
200+
var client = new VideoClient(CreateMockHttpClient(response));
201+
var result = await client.CreateAsync(new CreateVideoRequest { Model = "video-model" });
202+
203+
Assert.Equal("video_1", result.Id);
204+
}
205+
}

0 commit comments

Comments
 (0)