Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7a2d9cb
feat: moved examples from api-usage-examples repo
markitosha Mar 18, 2026
e1f44a3
docs: enhance README with detailed use cases and examples for Corti p…
markitosha Mar 18, 2026
f242c42
feat: moved demos from Dictation WC repo
markitosha Mar 18, 2026
47be6ef
Potential fix for pull request finding
markitosha Mar 18, 2026
da55930
Apply suggestions from code review
markitosha Mar 18, 2026
3871210
feat: reuse WebSocket across dictation sessions
markitosha Mar 18, 2026
e2c11fd
feat: enhance socket connection logic and configuration handling
markitosha Mar 18, 2026
2544957
docs: update README to clarify WebSocket connection and audio handling
markitosha Mar 18, 2026
a2d7608
feat: moved proxy implementation example
markitosha Mar 19, 2026
3479b72
feat: refactor Corti client initialization and streamline message han…
markitosha Mar 23, 2026
dc136a6
feat: add transcribe and stream endpoints with configuration options
markitosha Mar 24, 2026
650c758
feat: refactor Stream and Transcribe endpoints to streamline TaskComp…
markitosha Mar 25, 2026
edafe27
fix: correct method name from authorizeUrl to authorizeURL in token a…
markitosha Mar 25, 2026
1b12589
feat: update coding system in predict response and enhance JSON body …
markitosha Mar 25, 2026
ca486a2
feat: update coding system in CodesEndpoint and refactor Corti client…
markitosha Mar 27, 2026
1d93071
Merge branch 'main' of https://github.com/corticph/sdk-typescript-exa…
markitosha Mar 27, 2026
135f925
fix: update Corti.Sdk package reference version to use wildcard
markitosha Mar 27, 2026
ef1ebd0
feat: add ambient async endpoints and update Postman collection
markitosha Apr 8, 2026
53a8b46
feat: update @corti/sdk dependency to release candidate and refactor …
markitosha Apr 8, 2026
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
2 changes: 1 addition & 1 deletion sdk/dotnet/web-api/Corti.Sdk.Examples.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<ItemGroup>
<!-- Corti.Sdk: use local project if local.Corti.props sets CortiProjectPath, else NuGet (CI and default). -->
<ProjectReference Include="$(CortiProjectPath)" Condition="'$(CortiProjectPath)' != ''" />
<PackageReference Include="Corti.Sdk" Version="0.0.1" Condition="'$(CortiProjectPath)' == ''" />
<PackageReference Include="Corti.Sdk" Version="*" Condition="'$(CortiProjectPath)' == ''" />
<PackageReference Include="DotNetEnv" Version="3.1.1" />
<Content Include="sample\trouble-breathing.mp3" CopyToOutputDirectory="PreserveNewest" Link="sample\trouble-breathing.mp3" />
</ItemGroup>
Expand Down
102 changes: 102 additions & 0 deletions sdk/dotnet/web-api/Endpoints/AmbientAsyncEndToEndEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using Corti;
using CortiApiExamples;

namespace CortiApiExamples.Endpoints;

public static class AmbientAsyncEndToEndEndpoint
{
public static void MapAmbientAsyncEndToEndEndpoint(this WebApplication app)
{
app.MapGet("/ambient-async-end-to-end", Handle);
}

private static async Task<IResult> Handle(
IConfiguration config,
IWebHostEnvironment env)
{
if (!CortiHelpers.TryCreateCortiClient(config, out var client, out var credentialError))
{
return credentialError;
}

try
{
var samplePath = CortiHelpers.ResolveSampleFilePath(env.ContentRootPath, "trouble-breathing.mp3");
if (samplePath is null)
{
return Results.BadRequest(new
{
error =
"Sample file not found. Copy trouble-breathing.mp3 to sample/ or use typescript/next/public/trouble-breathing.mp3.",
});
}

var interactionId = Guid.NewGuid().ToString();
var interaction = await client!.Interactions.CreateAsync(
new InteractionsCreateRequest
{
Encounter = new InteractionsEncounterCreateRequest
{
Identifier = interactionId,
Status = InteractionsEncounterStatusEnum.Planned,
Type = InteractionsEncounterTypeEnum.FirstConsultation,
},
}
);

await using var audioStream = File.OpenRead(samplePath);
var recording = await client.Recordings.UploadAsync(interaction.InteractionId, audioStream);

var transcript = await client.Transcripts.CreateAsync(
interaction.InteractionId,
new TranscriptsCreateRequest
{
RecordingId = recording.RecordingId,
PrimaryLanguage = "en",
}
);

var document = await client.Documents.CreateAsync(
interaction.InteractionId,
DocumentsCreateRequest.FromDocumentsCreateRequestWithTemplateKey(
new DocumentsCreateRequestWithTemplateKey
{
Context = (transcript.Transcripts ?? Enumerable.Empty<CommonTranscriptResponse>())
.Select(t => DocumentsContext.FromDocumentsContextWithTranscript(
new DocumentsContextWithTranscript
{
Type = DocumentsContextWithTranscriptType.Transcript,
Data = new CommonTranscriptRequest
{
Channel = t.Channel,
Participant = t.Participant,
SpeakerId = t.SpeakerId,
Text = t.Text,
Start = t.Start,
End = t.End,
},
}
))
.ToArray(),
TemplateKey = "soap",
OutputLanguage = "en",
}
)
);

return Results.Ok(new
{
interactionId = interaction.InteractionId,
recordingId = recording.RecordingId,
transcript,
document,
documentName = document.Name,
});
}
catch (CortiClientApiException ex)
{
return CortiHelpers.CortiApiErrorResult(ex);
}
}
}

90 changes: 90 additions & 0 deletions sdk/dotnet/web-api/Endpoints/AmbientAsyncFactsEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Corti;
using CortiApiExamples;

namespace CortiApiExamples.Endpoints;

public static class AmbientAsyncFactsEndpoint
{
public static void MapAmbientAsyncFactsEndpoint(this WebApplication app)
{
app.MapGet("/ambient-async-facts", Handle);
}

private static async Task<IResult> Handle(
IConfiguration config,
IWebHostEnvironment env)
{
if (!CortiHelpers.TryCreateCortiClient(config, out var client, out var credentialError))
{
return credentialError;
}

try
{
var samplePath = CortiHelpers.ResolveSampleFilePath(env.ContentRootPath, "trouble-breathing.mp3");
if (samplePath is null)
{
return Results.BadRequest(new
{
error =
"Sample file not found. Copy trouble-breathing.mp3 to sample/ or use typescript/next/public/trouble-breathing.mp3.",
});
}

var interaction = await client!.Interactions.CreateAsync(
new InteractionsCreateRequest
{
Encounter = new InteractionsEncounterCreateRequest
{
Identifier = Guid.NewGuid().ToString(),
Status = InteractionsEncounterStatusEnum.Planned,
Type = InteractionsEncounterTypeEnum.FirstConsultation,
},
}
);

await using var audioStream = File.OpenRead(samplePath);
var recording = await client.Recordings.UploadAsync(interaction.InteractionId, audioStream);

var transcript = await client.Transcripts.CreateAsync(
interaction.InteractionId,
new TranscriptsCreateRequest
{
RecordingId = recording.RecordingId,
PrimaryLanguage = "en",
Diarize = true,
IsMultichannel = false,
}
);

var context = new[]
{
new CommonTextContext
{
Type = CommonTextContextType.Text,
Text = string.Join(" ", (transcript.Transcripts ?? Enumerable.Empty<CommonTranscriptResponse>()).Select(t => t.Text)),
},
};

var factsResponse = await client.Facts.ExtractAsync(new FactsExtractRequest
{
Context = context,
OutputLanguage = "en",
});

return Results.Ok(new
{
interactionId = interaction.InteractionId,
recordingId = recording.RecordingId,
transcript,
facts = factsResponse.Facts,
factCount = factsResponse.Facts.Count(),
message = "Ambient async facts (SDK): upload recording, transcribe, extract facts.",
});
}
catch (CortiClientApiException ex)
{
return CortiHelpers.CortiApiErrorResult(ex);
}
}
}
142 changes: 142 additions & 0 deletions sdk/dotnet/web-api/Endpoints/AmbientRtStreamsEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using Corti;
using CortiApiExamples;

namespace CortiApiExamples.Endpoints;

public static class AmbientRtStreamsEndpoint
{
private const int ChunkSize = 32_000;

public static void MapAmbientRtStreamsEndpoint(this WebApplication app)
{
app.MapGet("/ambient-rt-streams", Handle);
}

private static async Task<IResult> Handle(
IConfiguration config,
IWebHostEnvironment env)
{
if (!CortiHelpers.TryGetCortiConfig(config, out var cc, out var credentialError))
{
return credentialError;
}

var client = new CortiClient(
cc!.TenantName,
cc.Environment,
new CortiClientAuth.ClientCredentials(cc.ClientId, cc.ClientSecret)
);

var samplePath = CortiHelpers.ResolveSampleFilePath(env.ContentRootPath, "trouble-breathing.mp3");
if (samplePath is null)
{
return Results.BadRequest(new
{
error = "Sample file not found. Copy typescript/next/public/trouble-breathing.mp3 to csharp/api/sample/.",
});
}

try
{
var now = DateTime.UtcNow;
var interaction = await client.Interactions.CreateAsync(
new InteractionsCreateRequest
{
AssignedUserId = Guid.NewGuid().ToString(),
Encounter = new InteractionsEncounterCreateRequest
{
Identifier = Guid.NewGuid().ToString(),
Status = InteractionsEncounterStatusEnum.Planned,
Type = InteractionsEncounterTypeEnum.FirstConsultation,
Period = new InteractionsEncounterPeriod
{
StartedAt = now,
EndedAt = now,
},
Title = "Consultation",
},
}
);

await using var stream = await client.CreateStreamApiAsync(interaction.InteractionId);

var messages = new List<object>();
var endedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

void AddMessage(object msg)
{
lock (messages)
{
messages.Add(msg);
}
}

stream.StreamConfigStatusMessage.Subscribe(AddMessage);
stream.StreamTranscriptMessage.Subscribe(AddMessage);
stream.StreamFactsMessage.Subscribe(AddMessage);
stream.StreamUsageMessage.Subscribe(AddMessage);
stream.StreamErrorMessage.Subscribe(AddMessage);
stream.StreamEndedMessage.Subscribe(msg =>
{
AddMessage(msg);
endedTcs.TrySetResult();
});

await stream.ConnectAsync(new StreamConfig
{
Transcription = new StreamConfigTranscription
{
PrimaryLanguage = "en",
IsDiarization = false,
IsMultichannel = false,
Participants = new List<StreamConfigParticipant>
{
new() { Channel = 0, Role = StreamConfigParticipantRole.Multiple },
},
},
Mode = new StreamConfigMode
{
Type = StreamConfigModeType.Facts,
OutputLocale = "en",
},
});

await using (var audioStream = File.OpenRead(samplePath))
{
var buffer = new byte[ChunkSize];
int read;
while ((read = await audioStream.ReadAsync(buffer, CancellationToken.None)) > 0)
{
if (read == buffer.Length)
{
await stream.Send(buffer);
}
else
{
var chunk = new byte[read];
Buffer.BlockCopy(buffer, 0, chunk, 0, read);
await stream.Send(chunk);
}
}
}

await stream.Send(new StreamEndMessage());
await endedTcs.Task;

await stream.CloseAsync();

return Results.Ok(new
{
interactionId = interaction.InteractionId,
messageCount = messages.Count,
messages,
message = "Ambient RT streams (SDK): connect with facts config, stream audio, end, await ENDED.",
});
}
catch (CortiClientApiException ex)
{
return CortiHelpers.CortiApiErrorResult(ex);
}
}
}

3 changes: 1 addition & 2 deletions sdk/dotnet/web-api/Endpoints/CodesEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ private static async Task<IResult> Handle(IConfiguration config)
{
var predictResponse = await client!.Codes.PredictAsync(new CodesGeneralPredictRequest
{
System = [CommonCodingSystemEnum.Icd10Cm, CommonCodingSystemEnum.Cpt],
System = [CommonCodingSystemEnum.Icd10CmOutpatient, CommonCodingSystemEnum.Cpt],
Context =
[
new CommonTextContext
Expand All @@ -30,7 +30,6 @@ private static async Task<IResult> Handle(IConfiguration config)
Text = "Short arm splint applied in ED for pain control.",
},
],
MaxCandidates = 5,
});

return Results.Ok(new
Expand Down
Loading
Loading