Skip to content

Commit 30f4d01

Browse files
committed
Address pull-request review comments
1 parent 60ae1c2 commit 30f4d01

File tree

13 files changed

+270
-95
lines changed

13 files changed

+270
-95
lines changed

sdk/cs/src/EpInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Microsoft.AI.Foundry.Local;
1313
/// </summary>
1414
public record EpInfo
1515
{
16-
/// <summary>The display name of the bootstrapper (e.g. "CUDA Execution Provider").</summary>
16+
/// <summary>The identifier of the bootstrapper/execution provider (e.g. "CUDAExecutionProvider").</summary>
1717
[JsonPropertyName("Name")]
1818
public required string Name { get; init; }
1919

sdk/cs/src/FoundryLocalManager.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,20 @@ public async Task<EpDownloadResult> DownloadAndRegisterEpsAsync(IEnumerable<stri
173173
.ConfigureAwait(false);
174174
}
175175

176+
/// <summary>
177+
/// Deprecated compatibility wrapper for <see cref="DownloadAndRegisterEpsAsync(IEnumerable{string}?, CancellationToken?)"/>.
178+
/// </summary>
179+
/// <param name="names">
180+
/// Optional subset of EP bootstrapper names to download (as returned by <see cref="DiscoverEps"/>).
181+
/// If null or empty, all discoverable EPs are downloaded.
182+
/// </param>
183+
/// <param name="ct">Optional cancellation token.</param>
184+
[Obsolete("EnsureEpsDownloadedAsync has been replaced by DownloadAndRegisterEpsAsync and will be removed in a future release.")]
185+
public async Task EnsureEpsDownloadedAsync(IEnumerable<string>? names = null, CancellationToken? ct = null)
186+
{
187+
_ = await DownloadAndRegisterEpsAsync(names, ct).ConfigureAwait(false);
188+
}
189+
176190
private FoundryLocalManager(Configuration configuration, ILogger logger)
177191
{
178192
_config = configuration ?? throw new ArgumentNullException(nameof(configuration));

sdk/js/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ You can explicitly discover and download execution providers using the `discover
4242
// Discover available EPs and their status
4343
const eps = manager.discoverEps();
4444
for (const ep of eps) {
45-
console.log(`${ep.Name} — registered: ${ep.IsRegistered}`);
45+
console.log(`${ep.name} — registered: ${ep.isRegistered}`);
4646
}
4747

4848
// Download and register all available EPs
4949
const result = manager.downloadAndRegisterEps();
50-
console.log(`Success: ${result.Success}, Status: ${result.Status}`);
50+
console.log(`Success: ${result.success}, Status: ${result.status}`);
5151

5252
// Download only specific EPs
53-
const result2 = manager.downloadAndRegisterEps([eps[0].Name]);
53+
const result2 = manager.downloadAndRegisterEps([eps[0].name]);
5454
```
5555

5656
Catalog access does not block on EP downloads. Call `downloadAndRegisterEps()` when you need hardware-accelerated execution providers.

sdk/js/docs/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,70 @@ object: string;
153153

154154
***
155155

156+
### EpDownloadResult
157+
158+
Result of an explicit EP download and registration operation.
159+
160+
#### Properties
161+
162+
##### failedEps
163+
164+
```ts
165+
failedEps: string[];
166+
```
167+
168+
Names of EPs that failed to register.
169+
170+
##### registeredEps
171+
172+
```ts
173+
registeredEps: string[];
174+
```
175+
176+
Names of EPs that were successfully registered.
177+
178+
##### status
179+
180+
```ts
181+
status: string;
182+
```
183+
184+
Human-readable status message.
185+
186+
##### success
187+
188+
```ts
189+
success: boolean;
190+
```
191+
192+
True if all requested EPs were successfully downloaded and registered.
193+
194+
***
195+
196+
### EpInfo
197+
198+
Describes a discoverable execution provider bootstrapper.
199+
200+
#### Properties
201+
202+
##### isRegistered
203+
204+
```ts
205+
isRegistered: boolean;
206+
```
207+
208+
True if this EP has already been successfully downloaded and registered.
209+
210+
##### name
211+
212+
```ts
213+
name: string;
214+
```
215+
216+
The identifier of the bootstrapper/execution provider (e.g. "CUDAExecutionProvider").
217+
218+
***
219+
156220
### FoundryLocalConfig
157221

158222
Configuration options for the Foundry Local SDK.

sdk/js/docs/classes/FoundryLocalManager.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,44 @@ Error - If the web service is not running.
8787
8888
***
8989
90+
### discoverEps()
91+
92+
```ts
93+
discoverEps(): EpInfo[];
94+
```
95+
96+
Discovers available execution providers (EPs) and their registration status.
97+
98+
#### Returns
99+
100+
[`EpInfo`](../README.md#epinfo)[]
101+
102+
An array of EpInfo describing each available EP.
103+
104+
***
105+
106+
### downloadAndRegisterEps()
107+
108+
```ts
109+
downloadAndRegisterEps(names?): EpDownloadResult;
110+
```
111+
112+
Downloads and registers execution providers. This is a blocking call.
113+
114+
#### Parameters
115+
116+
| Parameter | Type | Description |
117+
| ------ | ------ | ------ |
118+
| `names?` | `string`[] | Optional array of EP names to download. If omitted, all available EPs are downloaded. |
119+
120+
#### Returns
121+
122+
[`EpDownloadResult`](../README.md#epdownloadresult)
123+
124+
An EpDownloadResult with the outcome of the operation.
125+
126+
***
127+
90128
### startWebService()
91129
92130
```ts

sdk/js/examples/chat-completion.ts

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,31 @@
44
// -------------------------------------------------------------------------
55

66
import { FoundryLocalManager } from '../src/index.js';
7-
import path from 'path';
87

98
async function main() {
10-
let modelToLoad: any = null;
11-
129
try {
1310
// Initialize the Foundry Local SDK
1411
console.log('Initializing Foundry Local SDK...');
1512

13+
// NOTE: You must update libraryPath to point to your built DLL if not in standard location
1614
const manager = FoundryLocalManager.create({
17-
appName: 'FoundryLocalAudioExample',
15+
appName: 'FoundryLocalExample',
16+
serviceEndpoint: 'http://localhost:5000',
1817
logLevel: 'info'
1918
});
2019
console.log('✓ SDK initialized successfully');
2120

21+
const availableEps = manager.discoverEps();
22+
console.log(`\nAvailable execution providers: ${availableEps.map((ep) => ep.name).join(', ')}`);
23+
24+
console.log('\nDownloading and registering execution providers...');
25+
const downloadResult = await manager.downloadAndRegisterEps();
26+
if (downloadResult.success) {
27+
console.log('✓ All execution providers registered successfully');
28+
} else {
29+
console.log(`⚠️ Some execution providers failed to download and/or register: ${downloadResult.failedEps.join(', ')}`);
30+
}
31+
2232
// Explore available models
2333
console.log('\nFetching available models...');
2434
const catalog = manager.catalog;
@@ -30,45 +40,44 @@ async function main() {
3040
console.log(` - ${model.alias} (variants: ${variants})`);
3141
}
3242

33-
const modelAlias = 'whisper-medium';
43+
// Explore cached models
44+
console.log('\nFetching cached models...');
45+
const cachedModels = await catalog.getCachedModels();
46+
console.log(`Found ${cachedModels.length} cached models:`);
47+
for (const cachedModel of cachedModels) {
48+
console.log(` - ${cachedModel.alias}`);
49+
}
50+
51+
const modelAlias = 'MODEL_ALIAS'; // Replace with a valid model alias from the list above
3452

35-
// Get the Whisper model
53+
// Load the model first
3654
console.log(`\nLoading model ${modelAlias}...`);
37-
modelToLoad = await catalog.getModel(modelAlias);
55+
const modelToLoad = await catalog.getModel(modelAlias);
3856
if (!modelToLoad) {
3957
throw new Error(`Model ${modelAlias} not found`);
4058
}
4159

42-
// Download if not cached
43-
if (!modelToLoad.isCached) {
44-
console.log('Downloading model...');
45-
await modelToLoad.download((progress: number) => {
46-
process.stdout.write(`\rDownload: ${progress.toFixed(1)}%`);
47-
});
48-
console.log();
49-
}
50-
5160
await modelToLoad.load();
5261
console.log('✓ Model loaded');
5362

54-
// Create audio client
55-
console.log('\nCreating audio client...');
56-
const audioClient = modelToLoad.createAudioClient();
63+
// Create chat client
64+
console.log('\nCreating chat client...');
65+
const chatClient = modelToLoad.createChatClient();
5766

58-
// Configure settings
59-
audioClient.settings.language = 'en';
60-
// audioClient.settings.temperature = 0.0; // deterministic results
67+
// Configure chat settings
68+
chatClient.settings.temperature = 0.7;
69+
chatClient.settings.maxTokens = 800;
6170

62-
console.log('✓ Audio client created');
71+
console.log('✓ Chat client created');
6372

64-
// Audio file path — update this to point to your audio file
65-
const audioFilePath = path.join(process.cwd(), '..', 'testdata', 'Recording.mp3');
73+
// Example chat completion
74+
console.log('\nTesting chat completion...');
75+
const completion = await chatClient.completeChat([
76+
{ role: 'user', content: 'What is the capital of France?' }
77+
]);
6678

67-
// Example: Standard transcription
68-
console.log('\nTesting standard transcription...');
69-
const result = await audioClient.transcribe(audioFilePath);
70-
console.log('\nTranscription result:');
71-
console.log(result.text);
79+
console.log('\nChat completion result:');
80+
console.log(completion.choices[0]?.message?.content);
7281

7382
// Example streaming completion
7483
console.log('\nTesting streaming completion...');
@@ -82,27 +91,33 @@ async function main() {
8291
}
8392
console.log('\n');
8493

85-
// Unload the model
86-
console.log('Unloading model...');
87-
await modelToLoad.unload();
88-
console.log(`✓ Model unloaded`);
94+
// Model management example
95+
const model = await catalog.getModel(modelAlias);
96+
if (model) {
97+
console.log('\nModel management example:');
98+
// Already loaded above, but let's check status
99+
console.log(`Checking model: ${model.id}`);
100+
console.log(`✓ Model loaded: ${await model.isLoaded()}`);
101+
102+
// Unload when done
103+
console.log('Unloading model...');
104+
await model.unload();
105+
console.log(`✓ Model loaded: ${await model.isLoaded()}`);
106+
}
89107

90-
console.log('\n✓ Audio transcription example completed successfully');
108+
console.log('\n✓ Example completed successfully');
91109

92110
} catch (error) {
93111
console.log('Error running example:', error);
112+
// Print stack trace if available
94113
if (error instanceof Error && error.stack) {
95114
console.log(error.stack);
96115
}
97-
// Best-effort cleanup
98-
if (modelToLoad) {
99-
try { await modelToLoad.unload(); } catch { /* ignore */ }
100-
}
101116
process.exit(1);
102117
}
103118
}
104119

105120
// Run the example
106121
main().catch(console.error);
107122

108-
export { main };
123+
export { main };

sdk/js/src/foundryLocalManager.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,17 @@ export class FoundryLocalManager {
101101
*/
102102
public discoverEps(): EpInfo[] {
103103
const response = this.coreInterop.executeCommand("discover_eps");
104+
type RawEpInfo = {
105+
Name: string;
106+
IsRegistered: boolean;
107+
};
108+
104109
try {
105-
return JSON.parse(response) as EpInfo[];
110+
const raw = JSON.parse(response) as RawEpInfo[];
111+
return raw.map((ep) => ({
112+
name: ep.Name,
113+
isRegistered: ep.IsRegistered
114+
}));
106115
} catch (error) {
107116
throw new Error(`Failed to decode JSON response from discover_eps: ${error}. Response was: ${response}`);
108117
}
@@ -114,13 +123,27 @@ export class FoundryLocalManager {
114123
* @returns An EpDownloadResult with the outcome of the operation.
115124
*/
116125
public downloadAndRegisterEps(names?: string[]): EpDownloadResult {
117-
const params: any = {};
126+
const params: { Params?: { Names: string } } = {};
118127
if (names && names.length > 0) {
119128
params.Params = { Names: names.join(",") };
120129
}
121130
const response = this.coreInterop.executeCommand("download_and_register_eps", Object.keys(params).length > 0 ? params : undefined);
131+
132+
type RawEpDownloadResult = {
133+
Success: boolean;
134+
Status: string;
135+
RegisteredEps: string[];
136+
FailedEps: string[];
137+
};
138+
122139
try {
123-
return JSON.parse(response) as EpDownloadResult;
140+
const raw = JSON.parse(response) as RawEpDownloadResult;
141+
return {
142+
success: raw.Success,
143+
status: raw.Status,
144+
registeredEps: raw.RegisteredEps,
145+
failedEps: raw.FailedEps
146+
};
124147
} catch (error) {
125148
throw new Error(`Failed to decode JSON response from download_and_register_eps: ${error}. Response was: ${response}`);
126149
}

sdk/js/src/types.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,22 @@ export interface ToolChoice {
7373

7474
/** Describes a discoverable execution provider bootstrapper. */
7575
export interface EpInfo {
76-
/** The display name of the bootstrapper (e.g. "CUDA Execution Provider"). */
77-
Name: string;
76+
/** The identifier of the bootstrapper/execution provider (e.g. "CUDAExecutionProvider"). */
77+
name: string;
7878
/** True if this EP has already been successfully downloaded and registered. */
79-
IsRegistered: boolean;
79+
isRegistered: boolean;
8080
}
8181

8282
/** Result of an explicit EP download and registration operation. */
8383
export interface EpDownloadResult {
8484
/** True if all requested EPs were successfully downloaded and registered. */
85-
Success: boolean;
85+
success: boolean;
8686
/** Human-readable status message. */
87-
Status: string;
87+
status: string;
8888
/** Names of EPs that were successfully registered. */
89-
RegisteredEps: string[];
89+
registeredEps: string[];
9090
/** Names of EPs that failed to register. */
91-
FailedEps: string[];
91+
failedEps: string[];
9292
}
9393

9494
// ============================================================================

0 commit comments

Comments
 (0)