Skip to content

Commit 673b67f

Browse files
authored
Merge pull request #12 from sharpninja/copilot/create-specflow-tests
Add tensor-based `traditional-local` model and benchmarkable local model comparison
2 parents 24199df + f46d4bb commit 673b67f

24 files changed

+1348
-46
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ bld/
4747

4848
# Visual Studio 2017 auto generated files
4949
Generated\ Files/
50+
*.feature.cs
5051

5152
# MSTest test Results
5253
[Tt]est[Rr]esult*/

docs/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ BitNet b1.58 Sharp is a .NET 10 C# reference implementation of the paper-aligned
77
- A paper-aligned BitNet core model in `/src/BitNetSharp.Core`
88
- A decoder-only transformer implementation with `BitLinear`, `RmsNorm`, RoPE, causal attention, SwiGLU, and `BitNetTransformer`
99
- Microsoft Agent Framework-oriented hosting in `/src/BitNetSharp.App`
10+
- BenchmarkDotNet-based local model comparison in `/src/BitNetSharp.App`
1011
- Default American English interaction behavior
1112
- Seeded transformer inspection and ternary weight summaries
1213
- GitBook-formatted project documentation in `/docs`
@@ -23,6 +24,7 @@ dotnet test /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/BitNet-b1.58
2324
## Documentation map
2425

2526
- [Architecture](architecture.md)
27+
- [Benchmarking and model comparison](benchmarking.md)
2628
- [Implementation plan](implementation-plan.md)
2729
- [Releases and packaging](releases-and-packaging.md)
2830
- [Usage](usage.md)

docs/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- [BitNet b1.58 Sharp](README.md)
44
- [Architecture](architecture.md)
55
- [Implementation plan](implementation-plan.md)
6+
- [Benchmarking and model comparison](benchmarking.md)
67
- [Releases and packaging](releases-and-packaging.md)
78
- [Usage](usage.md)
89
- [Training and visualization](training-and-visualization.md)

docs/architecture.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ BitNet b1.58 Sharp targets the paper-aligned BitNet b1.58 decoder-only transform
2020

2121
The app registers a local `IChatClient` implementation so the paper-aligned BitNet model can be hosted through Agent Framework conventions while remaining runnable as a standalone console application.
2222

23+
The hosting layer now resolves multiple local model types behind the same agent wrapper:
24+
25+
- the seeded paper-aligned BitNet model
26+
- a traditional local tensor-based comparison model trained on the default corpus with `System.Numerics.Tensors`
27+
- local command models described by JSON configuration files
28+
29+
This lets BenchmarkDotNet measure host construction, querying, streaming, and local training through one shared path.
30+
2331
## Language and interaction model
2432

2533
The built-in vocabulary and command output default to American English. That keeps prompts, diagnostics, and help text aligned with the requirement for a primary U.S. English interface.

docs/benchmarking.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Benchmarking and model comparison
2+
3+
## Overview
4+
5+
`BitNetSharp.App` can now host more than one local model shape through the same Microsoft Agent Framework wrapper:
6+
7+
- `bitnet-b1.58-sharp` for the paper-aligned seeded transformer
8+
- `traditional-local` for a local tensor-based comparison model trained on the default training corpus
9+
- an absolute path to a local command model JSON file for other locally available models
10+
11+
The benchmark command uses BenchmarkDotNet to measure the same hosted-model operations that the SpecFlow scenarios exercise:
12+
13+
- training a selected trainable model on the default dataset
14+
- generating a response for a prompt
15+
- streaming a response for a prompt
16+
- building the agent host
17+
18+
## Run the built-in comparison benchmark
19+
20+
```bash
21+
dotnet run --configuration Release --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- benchmark --model=bitnet-b1.58-sharp --compare-model=traditional-local --prompt="how are you hosted"
22+
```
23+
24+
This runs the BenchmarkDotNet suite over both local models so their hosted response and host-construction costs can be compared directly.
25+
26+
## Train the traditional local model
27+
28+
```bash
29+
dotnet run --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- train --model=traditional-local
30+
```
31+
32+
The traditional local model trains over `BitNetTrainingCorpus.CreateDefaultExamples()` for 24 epochs using `System.Numerics.Tensors` softmax and dot-product primitives so it can be benchmarked and queried against the same dataset every time.
33+
34+
## Compare another local model
35+
36+
Pass the absolute path to a JSON file that describes how to execute a locally available model runner:
37+
38+
```bash
39+
dotnet run --configuration Release --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- benchmark --model=/absolute/path/to/local-model.json --compare-model=traditional-local
40+
```
41+
42+
Example configuration:
43+
44+
```json
45+
{
46+
"modelId": "my-local-model",
47+
"displayName": "My local model",
48+
"executablePath": "/absolute/path/to/model-runner",
49+
"arguments": [
50+
"--model",
51+
"/absolute/path/to/model.bin"
52+
],
53+
"promptTransport": "StandardInput",
54+
"primaryLanguage": "en-US"
55+
}
56+
```
57+
58+
### Prompt transport options
59+
60+
- `StandardInput`: the prompt is written to the process standard input
61+
- `FinalArgument`: the prompt is appended as the final command-line argument
62+
63+
This keeps model comparison local-only and avoids endpoint or API-key based integrations.

docs/usage.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ dotnet build /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/BitNet-b1.5
88

99
## Chat
1010

11-
The chat command inspects the paper-aligned transformer and reports its top next-token predictions for the supplied prompt.
11+
The chat command can host the seeded paper-aligned transformer or another local comparison model and report that model's response for the supplied prompt.
1212

1313
```bash
1414
dotnet run --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- chat "how are you hosted"
15+
dotnet run --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- chat "how are you hosted" --model=traditional-local
1516
```
1617

1718
Optional verbosity:
@@ -25,14 +26,31 @@ dotnet run --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src
2526

2627
```bash
2728
dotnet run --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- host
29+
dotnet run --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- host --model=traditional-local
2830
```
2931

30-
This command confirms that the application is wired for Microsoft Agent Framework hosting and reports the current language and verbosity configuration.
32+
This command confirms that the application is wired for Microsoft Agent Framework hosting and reports the selected model, language, and verbosity configuration.
3133

3234
## Transformer inspection
3335

3436
```bash
3537
dotnet run --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- visualize
3638
```
3739

38-
This command prints the current paper-model configuration and an aggregated ternary weight histogram across the transformer's `BitLinear` projections.
40+
This command prints the current model summary. When the selected model is the paper-aligned BitNet transformer, it also prints the ternary weight histogram across the transformer's `BitLinear` projections.
41+
42+
## Benchmark
43+
44+
```bash
45+
dotnet run --configuration Release --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- benchmark --model=bitnet-b1.58-sharp --compare-model=traditional-local --prompt="how are you hosted"
46+
```
47+
48+
This command runs BenchmarkDotNet over the same hosted-model operations covered by the SpecFlow scenarios so you can compare local models under one agent wrapper.
49+
50+
## Train the traditional comparison model
51+
52+
```bash
53+
dotnet run --project /home/runner/work/BitNet-b1.58-Sharp/BitNet-b1.58-Sharp/src/BitNetSharp.App/BitNetSharp.App.csproj -- train --model=traditional-local
54+
```
55+
56+
The paper-aligned transformer still reports that training is not implemented in this branch. The `traditional-local` model trains a small tensor-based local language model on the default corpus for 24 epochs so its training and query performance can be benchmarked on the same dataset.

src/BitNetSharp.App/BitNetAgentHost.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,44 @@ namespace BitNetSharp.App;
88

99
public sealed record BitNetHostSummary(
1010
string AgentName,
11+
string ModelId,
12+
string DisplayName,
1113
string PrimaryLanguage,
1214
string HostingFramework,
1315
VerbosityLevel Verbosity);
1416

1517
public static class BitNetAgentHost
1618
{
17-
public static IHost Build(BitNetPaperModel model)
19+
public static IHost Build(BitNetPaperModel model) => Build(new BitNetHostedAgentModel(model));
20+
21+
public static IHost Build(IHostedAgentModel model)
1822
{
1923
ArgumentNullException.ThrowIfNull(model);
2024

2125
var builder = Host.CreateApplicationBuilder();
22-
var chatClient = new BitNetChatClient(model);
26+
var chatClient = new HostedModelChatClient(model);
2327

2428
builder.Services.AddSingleton(model);
2529
builder.Services.AddSingleton<IChatClient>(chatClient);
2630
builder.Services.AddSingleton(new BitNetHostSummary(
27-
"bitnet-b1.58-sharp",
28-
model.Options.PrimaryLanguage,
31+
model.AgentName,
32+
model.ModelId,
33+
model.DisplayName,
34+
model.PrimaryLanguage,
2935
"Microsoft Agent Framework",
30-
model.Options.Verbosity));
36+
model.Verbosity));
3137

3238
builder.AddAIAgent(
33-
"bitnet-b1.58-sharp",
34-
"Respond in clear American English using the paper-aligned BitNet b1.58 transformer diagnostics.",
39+
model.AgentName,
40+
model.SystemPrompt,
3541
chatClient)
3642
.WithInMemorySessionStore();
3743

44+
if (model is BitNetHostedAgentModel bitNetModel)
45+
{
46+
builder.Services.AddSingleton(bitNetModel.Model);
47+
}
48+
3849
return builder.Build();
3950
}
4051
}

src/BitNetSharp.App/BitNetChatClient.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
1-
using BitNetSharp.Core;
21
using Microsoft.Extensions.AI;
32

43
namespace BitNetSharp.App;
54

6-
public sealed class BitNetChatClient(BitNetPaperModel model) : IChatClient
5+
public sealed class HostedModelChatClient(IHostedAgentModel model) : IChatClient
76
{
8-
public Task<ChatResponse> GetResponseAsync(
7+
public async Task<ChatResponse> GetResponseAsync(
98
IEnumerable<ChatMessage> messages,
109
ChatOptions? options = null,
1110
CancellationToken cancellationToken = default)
1211
{
1312
var prompt = messages.LastOrDefault(message => message.Role == ChatRole.User)?.Text ?? string.Empty;
14-
var result = model.GenerateResponse(prompt, options?.MaxOutputTokens);
15-
var response = new ChatResponse(new ChatMessage(ChatRole.Assistant, result.ResponseText))
13+
var result = await model.GetResponseAsync(prompt, options?.MaxOutputTokens, cancellationToken);
14+
var response = new ChatResponse(new ChatMessage(ChatRole.Assistant, result.Text))
1615
{
1716
ModelId = model.ModelId,
1817
FinishReason = ChatFinishReason.Stop,
1918
CreatedAt = DateTimeOffset.UtcNow
2019
};
2120

22-
return Task.FromResult(response);
21+
return response;
2322
}
2423

2524
public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using BitNetSharp.Core;
2+
using BitNetSharp.Core.Quantization;
3+
4+
namespace BitNetSharp.App;
5+
6+
public sealed class BitNetHostedAgentModel(BitNetPaperModel model) : IHostedAgentModel, IInspectableHostedAgentModel
7+
{
8+
public BitNetPaperModel Model { get; } = model ?? throw new ArgumentNullException(nameof(model));
9+
10+
public string AgentName => Model.ModelId;
11+
12+
public string ModelId => Model.ModelId;
13+
14+
public string DisplayName => "Paper-aligned BitNet b1.58 transformer";
15+
16+
public string PrimaryLanguage => Model.Options.PrimaryLanguage;
17+
18+
public VerbosityLevel Verbosity => Model.Options.Verbosity;
19+
20+
public string SystemPrompt => "Respond in clear American English using the paper-aligned BitNet b1.58 transformer diagnostics.";
21+
22+
public IReadOnlyList<string> DescribeModel() =>
23+
[
24+
DisplayName,
25+
$"Model ID: {ModelId}",
26+
$"Vocabulary size: {Model.Config.VocabSize}",
27+
$"Layers: {Model.Config.LayerCount}",
28+
$"Dimension: {Model.Config.Dimension}",
29+
$"Hidden dimension: {Model.Config.HiddenDimension}",
30+
$"Heads: {Model.Config.HeadCount}",
31+
$"Max sequence length: {Model.Config.MaxSequenceLength}"
32+
];
33+
34+
public Task<HostedAgentModelResponse> GetResponseAsync(
35+
string prompt,
36+
int? maxOutputTokens = null,
37+
CancellationToken cancellationToken = default)
38+
{
39+
cancellationToken.ThrowIfCancellationRequested();
40+
var result = Model.GenerateResponse(prompt, maxOutputTokens);
41+
return Task.FromResult(new HostedAgentModelResponse(result.ResponseText, result.Diagnostics));
42+
}
43+
44+
public TernaryWeightStats GetTernaryWeightStats() => Model.GetTernaryWeightStats();
45+
46+
public void Dispose()
47+
{
48+
}
49+
}

src/BitNetSharp.App/BitNetSharp.App.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
</ItemGroup>
66

77
<ItemGroup>
8+
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
89
<PackageReference Include="Microsoft.Agents.AI" Version="1.0.0-rc4" />
910
<PackageReference Include="Microsoft.Agents.AI.Hosting" Version="1.0.0-preview.260311.1" />
1011
</ItemGroup>

0 commit comments

Comments
 (0)