├── .github └── workflows │ └── nuget.yml ├── .gitignore ├── DeepSeek.sln ├── LICENSE ├── README.md ├── README_cn.md ├── logo.jpg ├── pack.ps1 ├── sample ├── AspNetCoreSample │ ├── AspNetCoreSample.csproj │ ├── AspNetCoreSample.http │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.Development.json │ └── appsettings.json └── Sample │ ├── Program.cs │ ├── Sample.csproj │ └── appsettings.json └── src ├── DeepSeek.AspNetCore ├── DeepSeek.AspNetCore.csproj └── Extension.cs └── DeepSeek.Core ├── Constant.cs ├── DeepSeek.Core.csproj ├── DeepSeekClient.cs └── Models ├── ChatRequest.cs ├── ChatResponse.cs ├── CompletionRequest.cs ├── Message.cs ├── ModelResponse.cs ├── SourceGenerationContext.cs └── UserResponse.cs /.github/workflows/nuget.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: publish 5 | 6 | on: 7 | push: 8 | branches: [ "nuget" ] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Setup .NET 18 | uses: actions/setup-dotnet@v3.0.3 19 | with: 20 | dotnet-version: | 21 | 8.0.x 22 | 23 | - name: Restore dependencies 24 | run: dotnet restore 25 | 26 | - name: Build 27 | run: | 28 | dotnet build -c Release ./src/DeepSeek.Core/DeepSeek.Core.csproj 29 | 30 | - name: Pack 31 | run: | 32 | dotnet pack ./src/DeepSeek.Core/DeepSeek.Core.csproj -o ./pack 33 | 34 | - name: Publish to Nuget 35 | run: | 36 | dotnet nuget push ./pack/ -k ${{ vars.NUGET_KEY }} --source https://api.nuget.org/v3/index.json 37 | # dotnet nuget push ./pack/ -k ${{ vars.PAT }} --source https://nuget.pkg.github.com/niltor/index.json 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | -------------------------------------------------------------------------------- /DeepSeek.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.10.34902.84 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeepSeek.Core", "src\DeepSeek.Core\DeepSeek.Core.csproj", "{7F3AFFF3-3B8C-4DD5-B734-7F30C865969D}" 6 | EndProject 7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7CD120D2-ADBB-4C29-88D4-E1F80368EF2F}" 8 | EndProject 9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{FB0DAC5D-DB16-4FD9-8016-B07AF578711E}" 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeepSeek.AspNetCore", "src\DeepSeek.AspNetCore\DeepSeek.AspNetCore.csproj", "{A36B7B03-EF50-4C1B-AC43-64E2C234A97B}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "sample\Sample\Sample.csproj", "{89734A6C-32EA-49E1-96EC-BD7F5A756B8D}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreSample", "sample\AspNetCoreSample\AspNetCoreSample.csproj", "{33F8C39F-27C4-4507-80E4-24072A991286}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {7F3AFFF3-3B8C-4DD5-B734-7F30C865969D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {7F3AFFF3-3B8C-4DD5-B734-7F30C865969D}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {7F3AFFF3-3B8C-4DD5-B734-7F30C865969D}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {7F3AFFF3-3B8C-4DD5-B734-7F30C865969D}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {A36B7B03-EF50-4C1B-AC43-64E2C234A97B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {A36B7B03-EF50-4C1B-AC43-64E2C234A97B}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {A36B7B03-EF50-4C1B-AC43-64E2C234A97B}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {A36B7B03-EF50-4C1B-AC43-64E2C234A97B}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {89734A6C-32EA-49E1-96EC-BD7F5A756B8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {89734A6C-32EA-49E1-96EC-BD7F5A756B8D}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {89734A6C-32EA-49E1-96EC-BD7F5A756B8D}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {89734A6C-32EA-49E1-96EC-BD7F5A756B8D}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {33F8C39F-27C4-4507-80E4-24072A991286}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {33F8C39F-27C4-4507-80E4-24072A991286}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {33F8C39F-27C4-4507-80E4-24072A991286}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {33F8C39F-27C4-4507-80E4-24072A991286}.Release|Any CPU.Build.0 = Release|Any CPU 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | GlobalSection(NestedProjects) = preSolution 44 | {7F3AFFF3-3B8C-4DD5-B734-7F30C865969D} = {7CD120D2-ADBB-4C29-88D4-E1F80368EF2F} 45 | {A36B7B03-EF50-4C1B-AC43-64E2C234A97B} = {7CD120D2-ADBB-4C29-88D4-E1F80368EF2F} 46 | {89734A6C-32EA-49E1-96EC-BD7F5A756B8D} = {FB0DAC5D-DB16-4FD9-8016-B07AF578711E} 47 | {33F8C39F-27C4-4507-80E4-24072A991286} = {FB0DAC5D-DB16-4FD9-8016-B07AF578711E} 48 | EndGlobalSection 49 | GlobalSection(ExtensibilityGlobals) = postSolution 50 | SolutionGuid = {BFB78AFB-4E01-4743-B2EB-A2061F2D4A88} 51 | EndGlobalSection 52 | EndGlobal 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ater_NilTor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DeepSeekSDK-NET 2 | 3 | ![NuGet Version](https://img.shields.io/nuget/v/Ater.DeepSeek.Core) 4 | 5 | [DeepSeek](https://www.deepseek.com) API SDK specifically for .NET developers 6 | 7 | [中文文档](./README_cn.md) 8 | 9 | ## 🚀 Features 10 | 11 | - [x] List models 12 | - [x] Chat & Chat streaming 13 | - [x] Completions & Completions streaming (beta) 14 | - [x] User balance 15 | - [x] Local model support 16 | - [x] ASP.NET Core integration support 17 | 18 | ## Usage Requirements 19 | 20 | ## Usage 21 | 22 | Please go to [official website](https://platform.deepseek.com/), register and apply for DeepSeek's ApiKey 23 | 24 | Supported .NET version: .NET8 25 | 26 | ### Install Nuget package 27 | 28 | [Ater.DeepSeek.Core](https://www.nuget.org/packages/Ater.DeepSeek.Core) 29 | 30 | ```shell 31 | dotnet add package Ater.DeepSeek.Core 32 | ``` 33 | 34 | ### Instantiate `DeepSeekClient` 35 | 36 | Two methods are provided for instantiation: 37 | 38 | ```csharp 39 | public DeepSeekClient(string apiKey); 40 | public DeepSeekClient(HttpClient http, string apiKey); 41 | ``` 42 | 43 | The first type only requires providing the 'apiKey' to create an instance; 44 | 45 | The second method provides a `HttpClient` parameter, which is suitable for maintaining the `HttpClient` through the `HttpClientFactory` and then instance it. 46 | 47 | > [!NOTE] 48 | The default timeout for internal HttpClient is 120 seconds, which can be set before sending the request using the 'SetTimeout()' method, or by using the 'CancellationTokeSource' to set the timeout for specific requests. 49 | 50 | > [!TIP] 51 | > If you want to call a local model, try customizing `HttpClient` and setting `BaseAddress` to the local address. 52 | 53 | ### Calling method 54 | 55 | `DeepSeekClient` class provides six asynchronous methods to call DeepSeek's API: 56 | 57 | ```csharp 58 | Task ListModelsAsync(CancellationToken cancellationToken); 59 | 60 | Task ChatAsync(ChatRequest request, CancellationToken cancellationToken); 61 | 62 | Task?> ChatStreamAsync(ChatRequest request, CancellationToken cancellationToken); 63 | 64 | Task CompletionsAsync(CompletionRequest request, CancellationToken cancellationToken); 65 | 66 | Task?> CompletionsStreamAsync(CompletionRequest request, CancellationToken cancellationToken); 67 | 68 | Task GetUserBalanceAsync(CancellationToken cancellationToken); 69 | 70 | ``` 71 | 72 | ### List Models Sample 73 | 74 | ```csharp 75 | // Create an instance using the apiKey 76 | var client = new DeepSeekClient(apiKey); 77 | 78 | var modelResponse = await client.ListModelsAsync(new CancellationToken()); 79 | if (modelResponse is null) 80 | { 81 | Console.WriteLine(client.ErrorMsg); 82 | return; 83 | } 84 | foreach (var model in modelResponse.Data) 85 | { 86 | Console.WriteLine(model); 87 | } 88 | ``` 89 | 90 | ### Chat Examples 91 | 92 | ```csharp 93 | // Create an instance using the apiKey 94 | var client = new DeepSeekClient(apiKey); 95 | // Construct the request body 96 | var request = new ChatRequest 97 | { 98 | Messages = [ 99 | Message.NewSystemMessage("You are a language translator"), 100 | Message.NewUserMessage("Please translate 'They are scared! ' into English!") 101 | ], 102 | // Specify the model 103 | Model = Constant.Model.ChatModel 104 | }; 105 | 106 | var chatResponse = await client.ChatAsync(request, new CancellationToken()); 107 | if (chatResponse is null) 108 | { 109 | Console.WriteLine(client.ErrorMsg); 110 | } 111 | Console.WriteLine(chatResponse?.Choices.First().Message?.Content); 112 | ``` 113 | 114 | ### Chat Examples (Stream) 115 | 116 | ```csharp 117 | // Create an instance using the apiKey 118 | var client = new DeepSeekClient(apiKey); 119 | // Construct the request body 120 | var request = new ChatRequest 121 | { 122 | Messages = [ 123 | Message.NewSystemMessage("You are a language translator"), 124 | Message.NewUserMessage("Please translate 'They are scared! ' into English!") 125 | ], 126 | // Specify the model 127 | Model = Constant.Model.ChatModel 128 | }; 129 | 130 | var choices = client.ChatStreamAsync(request, new CancellationToken()); 131 | if (choices is null) 132 | { 133 | Console.WriteLine(client.ErrorMsg); 134 | return; 135 | } 136 | await foreach (var choice in choices) 137 | { 138 | Console.Write(choice.Delta?.Content); 139 | } 140 | Console.WriteLine(); 141 | ``` 142 | 143 | ### Local Model Examples 144 | 145 | ```csharp 146 | // use local models api 147 | var httpClient = new HttpClient 148 | { 149 | // set your local api address 150 | BaseAddress = new Uri("http://localhost:5000"), 151 | Timeout = TimeSpan.FromSeconds(300), 152 | }; 153 | // if have api key 154 | // httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + "your_token"); 155 | 156 | var localClient = new DeepSeekClient(httpClient); 157 | localClient.SetChatEndpoint("/chat"); 158 | localClient.SetCompletionEndpoint("/completions"); 159 | 160 | var res = await localClient.ChatAsync(new ChatRequest 161 | { 162 | Messages = new List 163 | { 164 | Message.NewUserMessage("hello") 165 | } 166 | }, new CancellationToken()); 167 | 168 | return res?.Choices.First().Message?.Content; 169 | ``` 170 | 171 | > [!TIP] 172 | > More [usage example](https://github.com/niltor/DeepSeekSDK-NET/tree/dev/sample/Sample) 173 | 174 | ## ASP.NET Core Integration 175 | 176 | ### Install `Ater.DeepSeek.AspNetCore` package 177 | 178 | ```shell 179 | dotnet add package Ater.DeepSeek.AspNetCore 180 | ``` 181 | 182 | ### Usage in ASP.NET Core 183 | 184 | ```csharp 185 | using DeepSeek.AspNetCore; 186 | using DeepSeek.Core; 187 | using DeepSeek.Core.Models; 188 | using Microsoft.AspNetCore.Mvc; 189 | 190 | var builder = WebApplication.CreateBuilder(args); 191 | 192 | var apiKey = builder.Configuration["DeepSeekApiKey"]; 193 | builder.Services.AddDeepSeek(option => 194 | { 195 | option.BaseAddress = new Uri("https://api.deepseek.com"); 196 | option.Timeout = TimeSpan.FromSeconds(300); 197 | option.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + apiKey); 198 | }); 199 | 200 | var app = builder.Build(); 201 | 202 | app.MapGet("/test", async ([FromServices] DeepSeekClient client) => 203 | { 204 | var res = await client.ChatAsync(new ChatRequest 205 | { 206 | Messages = new List 207 | { 208 | Message.NewUserMessage("Why dotnet is good?") 209 | }, 210 | MaxTokens = 200 211 | }, new CancellationToken()); 212 | 213 | return res?.Choices.First().Message?.Content; 214 | }); 215 | 216 | app.Run(); 217 | ``` 218 | 219 | ### Usage in ASP.NET Core (Stream) 220 | 221 | ```csharp 222 | app.MapGet("/chat", async (HttpContext context, [FromServices] DeepSeekClient client, CancellationToken token) => 223 | { 224 | context.Response.ContentType = "text/text;charset=utf-8"; 225 | try 226 | { 227 | var choices = client.ChatStreamAsync(new ChatRequest 228 | { 229 | Messages = new List 230 | { 231 | Message.NewUserMessage("Why dotnet is good?") 232 | }, 233 | MaxTokens = 200 234 | }, token); 235 | 236 | if (choices != null) 237 | { 238 | await foreach (var choice in choices) 239 | { 240 | await context.Response.WriteAsync(choice.Delta!.Content); 241 | } 242 | } 243 | } 244 | catch (Exception ex) 245 | { 246 | await context.Response.WriteAsync("暂时无法提供服务" + ex.Message); 247 | } 248 | await context.Response.CompleteAsync(); 249 | }); 250 | ``` 251 | 252 | > [!TIP] 253 | > More [usage example](https://github.com/niltor/DeepSeekSDK-NET/tree/dev/sample/AspNetCoreSample) 254 | -------------------------------------------------------------------------------- /README_cn.md: -------------------------------------------------------------------------------- 1 | # DeepSeekSDK-NET 2 | 3 | ![NuGet Version](https://img.shields.io/nuget/v/Ater.DeepSeek.Core) 4 | 5 | 专门为.NET开发者提供的 [DeepSeek](https://www.deepseek.com) API SDK. 6 | 7 | [English Docs](./README.md) 8 | 9 | ## 🚀 功能特性 10 | 11 | - [x] 列出模型 12 | - [x] 对话补全(包含流式) 13 | - [x] FIM实例(包含流式) 14 | - [x] 查询余额 15 | - [x] 支持调用本地模型 16 | - [x] 对ASP.NET Core的集成支持 17 | 18 | ## 使用 19 | 20 | 请到[官方网站](https://platform.deepseek.com/),注册并申请DeepSeek的`ApiKey`. 21 | 22 | .NET版本:.NET8 23 | 24 | ### 安装Nuget包 25 | 26 | [Ater.DeepSeek.Core](https://www.nuget.org/packages/Ater.DeepSeek.Core) 27 | 28 | ```shell 29 | dotnet add package Ater.DeepSeek.Core 30 | ``` 31 | 32 | ### 实例化`DeepSeekClient` 33 | 34 | 提供了两种方式进行实例化: 35 | 36 | ```csharp 37 | public DeepSeekClient(string apiKey); 38 | public DeepSeekClient(HttpClient http, string apiKey); 39 | ``` 40 | 41 | 第一种只需要提供`apiKey`即可创建实例; 42 | 43 | 第二种提供了`HttpClient`参数,适合通过`HttpClientFactory`来维护`HttpClient`,然后进行实例化。 44 | 45 | > [!NOTE] 46 | > 内部HttpClient的超时时间默认为120秒,可通过`SetTimeout()`方法在发送请求前设置,或通过`CancellationTokenSource`设置具体请求的超时时间。 47 | 48 | > [!TIP] 49 | > 如果你想调用本地模型,可尝试自定义`HttpClient`,并设置`BaseAddress`为本地地址。 50 | 51 | ### 调用方法 52 | 53 | `DeepSeekClient`类提供了六个异步方法来调用DeepSeek的API: 54 | 55 | ```csharp 56 | Task ListModelsAsync(CancellationToken cancellationToken); 57 | 58 | Task ChatAsync(ChatRequest request, CancellationToken cancellationToken); 59 | 60 | Task?> ChatStreamAsync(ChatRequest request, CancellationToken cancellationToken); 61 | 62 | Task CompletionsAsync(CompletionRequest request, CancellationToken cancellationToken); 63 | 64 | Task?> CompletionsStreamAsync(CompletionRequest request, CancellationToken cancellationToken); 65 | 66 | Task GetUserBalanceAsync(CancellationToken cancellationToken); 67 | ``` 68 | 69 | ### 获取模型列表示例 70 | 71 | ```csharp 72 | // 通过apiKey创建实例 73 | var client = new DeepSeekClient(apiKey); 74 | 75 | var modelResponse = await client.ListModelsAsync(new CancellationToken()); 76 | if (modelResponse is null) 77 | { 78 | Console.WriteLine(client.ErrorMsg); 79 | return; 80 | } 81 | foreach (var model in modelResponse.Data) 82 | { 83 | Console.WriteLine(model); 84 | } 85 | ``` 86 | 87 | ### 获取对话示例 88 | 89 | ```csharp 90 | // 通过apiKey创建实例 91 | var client = new DeepSeekClient(apiKey); 92 | // 构造请求体 93 | var request = new ChatRequest 94 | { 95 | Messages = [ 96 | Message.NewSystemMessage("你是一个语言翻译家"), 97 | Message.NewUserMessage("请翻译'它们害怕极了!'为英语!") 98 | ], 99 | // 指定模型 100 | Model = Constant.Model.ChatModel 101 | }; 102 | 103 | var chatResponse = await client.ChatAsync(request, new CancellationToken()); 104 | if (chatResponse is null) 105 | { 106 | Console.WriteLine(client.ErrorMsg); 107 | } 108 | Console.WriteLine(chatResponse?.Choices.First().Message?.Content); 109 | ``` 110 | 111 | ### 获取对话(Stream) 112 | 113 | ```csharp 114 | // 通过apiKey创建实例 115 | var client = new DeepSeekClient(apiKey); 116 | // 构造请求体 117 | var request = new ChatRequest 118 | { 119 | Messages = [ 120 | Message.NewSystemMessage("你是一个语言翻译家"), 121 | Message.NewUserMessage("请翻译'它们害怕极了!'为英语!") 122 | ], 123 | // 指定模型 124 | Model = Constant.Model.ChatModel 125 | }; 126 | 127 | var choices = client.ChatStreamAsync(request, new CancellationToken()); 128 | if (choices is null) 129 | { 130 | Console.WriteLine(client.ErrorMsg); 131 | return; 132 | } 133 | await foreach (var choice in choices) 134 | { 135 | Console.Write(choice.Delta?.Content); 136 | } 137 | Console.WriteLine(); 138 | ``` 139 | 140 | ### 本地模型调用示例 141 | 142 | ```csharp 143 | // use local models api 144 | var httpClient = new HttpClient 145 | { 146 | // set your local api address 147 | BaseAddress = new Uri("http://localhost:5000"), 148 | Timeout = TimeSpan.FromSeconds(300), 149 | }; 150 | // if have api key 151 | // httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + "your_token"); 152 | 153 | var localClient = new DeepSeekClient(httpClient); 154 | localClient.SetChatEndpoint("/chat"); 155 | localClient.SetCompletionEndpoint("/completions"); 156 | 157 | var res = await localClient.ChatAsync(new ChatRequest 158 | { 159 | Messages = new List 160 | { 161 | Message.NewUserMessage("hello") 162 | } 163 | }, new CancellationToken()); 164 | return res?.Choices.First().Message?.Content; 165 | ``` 166 | 167 | > [!TIP] 168 | > 更多[使用示例](https://github.com/niltor/DeepSeekSDK-NET/tree/dev/sample/Sample). 169 | > 170 | 171 | ## 在ASP.NET Core中使用 172 | 173 | ### 安装`Ater.DeepSeek.AspNetCore`包 174 | 175 | ```shell 176 | dotnet add package Ater.DeepSeek.AspNetCore 177 | ``` 178 | 179 | ### 示例代码 180 | 181 | ```csharp 182 | using DeepSeek.AspNetCore; 183 | using DeepSeek.Core; 184 | using DeepSeek.Core.Models; 185 | using Microsoft.AspNetCore.Mvc; 186 | 187 | var builder = WebApplication.CreateBuilder(args); 188 | 189 | var apiKey = builder.Configuration["DeepSeekApiKey"]; 190 | builder.Services.AddDeepSeek(option => 191 | { 192 | option.BaseAddress = new Uri("https://api.deepseek.com"); 193 | option.Timeout = TimeSpan.FromSeconds(300); 194 | option.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + apiKey); 195 | }); 196 | 197 | var app = builder.Build(); 198 | 199 | app.MapGet("/test", async ([FromServices] DeepSeekClient client) => 200 | { 201 | var res = await client.ChatAsync(new ChatRequest 202 | { 203 | Messages = new List 204 | { 205 | Message.NewUserMessage("Why dotnet is good?") 206 | }, 207 | MaxTokens = 200 208 | }, new CancellationToken()); 209 | 210 | return res?.Choices.First().Message?.Content; 211 | }); 212 | 213 | app.Run(); 214 | ``` 215 | 216 | ### 流式返回示例 217 | 218 | ```csharp 219 | app.MapGet("/chat", async (HttpContext context, [FromServices] DeepSeekClient client, CancellationToken token) => 220 | { 221 | context.Response.ContentType = "text/text;charset=utf-8"; 222 | try 223 | { 224 | var choices = client.ChatStreamAsync(new ChatRequest 225 | { 226 | Messages = new List 227 | { 228 | Message.NewUserMessage("Why dotnet is good?") 229 | }, 230 | MaxTokens = 200 231 | }, token); 232 | 233 | if (choices != null) 234 | { 235 | await foreach (var choice in choices) 236 | { 237 | await context.Response.WriteAsync(choice.Delta!.Content); 238 | } 239 | } 240 | } 241 | catch (Exception ex) 242 | { 243 | await context.Response.WriteAsync("暂时无法提供服务" + ex.Message); 244 | } 245 | await context.Response.CompleteAsync(); 246 | }); 247 | ``` 248 | 249 | > [!TIP] 250 | > More [usage example](https://github.com/niltor/DeepSeekSDK-NET/tree/dev/sample/AspNetCoreSample) 251 | -------------------------------------------------------------------------------- /logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niltor/DeepSeekSDK-NET/ae660e8c6e5521b3fe3a715597313e92b0343a60/logo.jpg -------------------------------------------------------------------------------- /pack.ps1: -------------------------------------------------------------------------------- 1 | dotnet pack .\src\DeepSeek.AspNetCore\ -c release -o ./pack 2 | 3 | dotnet pack .\src\DeepSeek.Core\ -c release -o ./pack -------------------------------------------------------------------------------- /sample/AspNetCoreSample/AspNetCoreSample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 35c4eb98-692e-46d2-9f00-6e34a9bb5a4c 8 | True 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sample/AspNetCoreSample/AspNetCoreSample.http: -------------------------------------------------------------------------------- 1 | @AspNetCoreSample_HostAddress = http://localhost:5195 2 | 3 | GET {{AspNetCoreSample_HostAddress}}/weatherforecast/ 4 | Accept: application/json 5 | 6 | ### 7 | -------------------------------------------------------------------------------- /sample/AspNetCoreSample/Program.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/niltor/DeepSeekSDK-NET/ae660e8c6e5521b3fe3a715597313e92b0343a60/sample/AspNetCoreSample/Program.cs -------------------------------------------------------------------------------- /sample/AspNetCoreSample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:43559", 8 | "sslPort": 0 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "/", 17 | "applicationUrl": "http://localhost:5195", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "weatherforecast", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sample/AspNetCoreSample/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sample/AspNetCoreSample/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "DeepSeekApiKey": "" 10 | } 11 | -------------------------------------------------------------------------------- /sample/Sample/Program.cs: -------------------------------------------------------------------------------- 1 | using DeepSeek.Core; 2 | using DeepSeek.Core.Models; 3 | using Microsoft.Extensions.Configuration; 4 | 5 | 6 | // 从appsettings.json读取秘钥 7 | var builder = new ConfigurationBuilder() 8 | .SetBasePath(Directory.GetCurrentDirectory()) 9 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 10 | .AddUserSecrets(); 11 | 12 | var configuration = builder.Build(); 13 | 14 | var apiKey = configuration["apiKey"]; 15 | 16 | if (apiKey == null) 17 | { 18 | Console.WriteLine("apiKey is null"); 19 | return; 20 | } 21 | 22 | // create client 23 | var client = new DeepSeekClient(apiKey); 24 | 25 | // get models 26 | var modelResponse = await client.ListModelsAsync(new CancellationToken()); 27 | if (modelResponse is null) 28 | { 29 | Console.WriteLine(client.ErrorMsg); 30 | return; 31 | } 32 | foreach (var model in modelResponse.Data) 33 | { 34 | Console.WriteLine(model); 35 | } 36 | 37 | //await ChatAsync(client); 38 | //await CompletionsAsync(client); 39 | //await GetUserBalanceAsync(client); 40 | await StreamChatAsync(client); 41 | //await TestLocalAsync(); 42 | 43 | Console.WriteLine("done"); 44 | Console.ReadLine(); 45 | 46 | // stream chat using DeepSeek-R1 47 | static async Task StreamChatAsync(DeepSeekClient client) 48 | { 49 | var request = new ChatRequest 50 | { 51 | Messages = [ 52 | Message.NewUserMessage("which is greater between 9.11 and 9.8?") 53 | ], 54 | //Model = DeepSeekModels.ChatModel 55 | Model = DeepSeekModels.ReasonerModel 56 | }; 57 | 58 | var choices = client.ChatStreamAsync(request, new CancellationToken()); 59 | if (choices is null) 60 | { 61 | Console.WriteLine(client.ErrorMsg); 62 | return; 63 | } 64 | await foreach (var choice in choices) 65 | { 66 | // output Cot 67 | if (!string.IsNullOrWhiteSpace(choice.Delta?.ReasoningContent)) 68 | { 69 | Console.WriteLine(choice.Delta.ReasoningContent); 70 | } 71 | else 72 | { 73 | Console.Write(choice.Delta?.Content); 74 | } 75 | } 76 | Console.WriteLine(); 77 | } 78 | 79 | // chat 80 | static async Task ChatAsync(DeepSeekClient client) 81 | { 82 | var request = new ChatRequest 83 | { 84 | Messages = [ 85 | Message.NewSystemMessage("你是一个语言翻译家"), 86 | Message.NewUserMessage(""" 87 | 请翻译'它们害怕极了!'为英语!,返回json,格式为: 88 | { 89 | "text":"", 90 | "translate":"" 91 | } 92 | """) 93 | ], 94 | ResponseFormat = new ResponseFormat 95 | { 96 | Type = ResponseFormatTypes.JsonObject 97 | }, 98 | Model = DeepSeekModels.ChatModel 99 | }; 100 | 101 | 102 | var chatResponse = await client.ChatAsync(request, new CancellationToken()); 103 | 104 | if (chatResponse is null) 105 | { 106 | Console.WriteLine(client.ErrorMsg); 107 | } 108 | // usage 109 | Console.WriteLine("use token:" + chatResponse?.Usage?.TotalTokens); 110 | // result 111 | Console.WriteLine(chatResponse?.Choices.FirstOrDefault()?.Message?.Content); 112 | } 113 | 114 | // completions 115 | static async Task CompletionsAsync(DeepSeekClient client) 116 | { 117 | var request = new CompletionRequest 118 | { 119 | Prompt = ".Net and C# is prefect, because", 120 | Model = DeepSeekModels.ChatModel, 121 | MaxTokens = 100 122 | }; 123 | var response = await client.CompletionsAsync(request, new CancellationToken()); 124 | if (response is null) 125 | { 126 | Console.WriteLine(client.ErrorMsg); 127 | return; 128 | } 129 | // usage 130 | Console.WriteLine(response?.Usage?.TotalTokens); 131 | // result 132 | Console.WriteLine(response?.Choices.First().Text); 133 | } 134 | 135 | // user balance 136 | static async Task GetUserBalanceAsync(DeepSeekClient client) 137 | { 138 | var balance = await client.GetUserBalanceAsync(new CancellationToken()); 139 | if (balance is null) 140 | { 141 | Console.WriteLine(client.ErrorMsg); 142 | return; 143 | } 144 | Console.WriteLine(balance.BalanceInfos.First().TotalBalance); 145 | } 146 | 147 | static async Task TestLocalAsync() 148 | { 149 | // use local models api 150 | var httpClient = new HttpClient 151 | { 152 | // set your local api address 153 | BaseAddress = new Uri("http://localhost:5000"), 154 | Timeout = TimeSpan.FromSeconds(300), 155 | }; 156 | // if have api key 157 | // httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + "your_token"); 158 | 159 | var localClient = new DeepSeekClient(httpClient); 160 | localClient.SetChatEndpoint("/chat"); 161 | localClient.SetCompletionEndpoint("/completions"); 162 | 163 | var res = await localClient.ChatAsync(new ChatRequest 164 | { 165 | Messages = new List 166 | { 167 | Message.NewUserMessage("hello") 168 | } 169 | }, new CancellationToken()); 170 | 171 | Console.WriteLine(res?.Choices.FirstOrDefault()?.Message); 172 | } -------------------------------------------------------------------------------- /sample/Sample/Sample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | e1065991-095b-47fe-8e75-38e70eba6243 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sample/Sample/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiKey": "" 3 | } 4 | -------------------------------------------------------------------------------- /src/DeepSeek.AspNetCore/DeepSeek.AspNetCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | enable 5 | enable 6 | latest 7 | NilTor,AterDev 8 | Geethin 9 | DeepSeek API SDK 10 | 11 | 1. Add ChatStreamWithStringAsync to get raw response. 12 | 13 | https://github.com/niltor/DeepSeekSDK-NET 14 | https://github.com/niltor/DeepSeekSDK-NET 15 | git 16 | LLM,AI,DeepSeek,API,SDK 17 | false 18 | logo.jpg 19 | 1.1.5 20 | Ater.DeepSeek.AspNetCore 21 | DeepSeek.AspNetCore 22 | MIT 23 | 24 | Library 25 | 1701;1702;1591;1570 26 | README.md 27 | ./pack 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | True 39 | \ 40 | 41 | 42 | True 43 | \ 44 | 45 | 46 | True 47 | \ 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/DeepSeek.AspNetCore/Extension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DeepSeek.Core; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace DeepSeek.AspNetCore; 6 | public static class Extension 7 | { 8 | public static IServiceCollection AddDeepSeek(this IServiceCollection services, Action option) 9 | { 10 | services.AddHttpClient(option); 11 | services.AddScoped(provider => 12 | { 13 | var httpClient = provider.GetRequiredService().CreateClient(nameof(DeepSeekClient)); 14 | return new DeepSeekClient(httpClient); 15 | }); 16 | return services; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/Constant.cs: -------------------------------------------------------------------------------- 1 | namespace DeepSeek.Core; 2 | 3 | [Obsolete("Use DeepSeekModels instead")] 4 | public class Constant 5 | { 6 | public class Model 7 | { 8 | public const string ChatModel = "deepseek-chat"; 9 | public const string CoderModel = "deepseek-coder"; 10 | } 11 | } 12 | 13 | public class DeepSeekModels 14 | { 15 | public const string ChatModel = "deepseek-chat"; 16 | [Obsolete] 17 | public const string CoderModel = "deepseek-coder"; 18 | public const string ReasonerModel = "deepseek-reasoner"; 19 | } 20 | 21 | public class ResponseFormatTypes 22 | { 23 | public const string Text = "text"; 24 | public const string JsonObject = "json_object"; 25 | } 26 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/DeepSeek.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | enable 5 | enable 6 | latest 7 | NilTor,AterDev 8 | Geethin 9 | DeepSeek API SDK 10 | 11 | 1. Add ChatStreamWithStringAsync to get raw response. 12 | 13 | https://github.com/niltor/DeepSeekSDK-NET 14 | https://github.com/niltor/DeepSeekSDK-NET 15 | git 16 | LLM,AI,DeepSeek,API,SDK 17 | false 18 | 0.1.0.0 19 | 0.1.0.0 20 | 21 | logo.jpg 22 | 1.1.5 23 | Ater.DeepSeek.Core 24 | MIT 25 | 26 | Library 27 | False 28 | 1701;1702;1591;1570 29 | DeepSeek.Core 30 | README.md 31 | False 32 | ./pack 33 | 22045c95-30db-48d5-862c-48f22ed56e0a 34 | 35 | 36 | 37 | True 38 | \ 39 | 40 | 41 | True 42 | \ 43 | 44 | 45 | True 46 | \ 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/DeepSeekClient.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Text; 3 | using System.Text.Encodings.Web; 4 | using System.Text.Json; 5 | using System.Text.Json.Serialization; 6 | using System.Text.Unicode; 7 | using System.Threading.Channels; 8 | using DeepSeek.Core.Models; 9 | 10 | namespace DeepSeek.Core; 11 | 12 | public class DeepSeekClient 13 | { 14 | /// 15 | /// base address 16 | /// 17 | public readonly string BaseAddress = "https://api.deepseek.com"; 18 | public readonly string BetaBaseAddress = "https://api.deepseek.com/beta"; 19 | /// 20 | /// chat endpoint 21 | /// 22 | public string ChatEndpoint { get; private set; } = "/chat/completions"; 23 | public string CompletionEndpoint { get; private set; } = "/completions"; 24 | public readonly string UserBalanceEndpoint = "/user/balance"; 25 | 26 | /// 27 | /// list models endpoint 28 | /// 29 | public readonly string ModelsEndpoint = "/models"; 30 | 31 | /// 32 | /// done sign 33 | /// 34 | private const string StreamDoneSign = "[DONE]"; 35 | 36 | protected readonly HttpClient Http; 37 | public JsonSerializerOptions JsonSerializerOptions = new() 38 | { 39 | ReferenceHandler = ReferenceHandler.IgnoreCycles, 40 | PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, 41 | TypeInfoResolver = SourceGenerationContext.Default, 42 | Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) 43 | }; 44 | 45 | public string? ErrorMsg { get; private set; } 46 | 47 | /// 48 | /// for dependency injection 49 | /// 50 | /// 51 | public DeepSeekClient(HttpClient httpClient) 52 | { 53 | Http = httpClient; 54 | } 55 | 56 | public DeepSeekClient(HttpClient http, string apiKey) 57 | { 58 | Http = http; 59 | if (http.BaseAddress is null) 60 | { 61 | http.BaseAddress = new Uri(BaseAddress); 62 | } 63 | if (Http.DefaultRequestHeaders.Authorization is null) 64 | { 65 | Http.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + apiKey); 66 | } 67 | } 68 | 69 | public DeepSeekClient(string apiKey) 70 | { 71 | Http = new HttpClient() 72 | { 73 | BaseAddress = new Uri(BaseAddress), 74 | Timeout = TimeSpan.FromSeconds(120), 75 | }; 76 | Http.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + apiKey); 77 | } 78 | 79 | public void SetTimeout(int seconds) 80 | { 81 | Http.Timeout = TimeSpan.FromSeconds(seconds); 82 | } 83 | public void SetChatEndpoint(string endpoint) 84 | { 85 | ChatEndpoint = endpoint; 86 | } 87 | 88 | public void SetCompletionEndpoint(string endpoint) 89 | { 90 | CompletionEndpoint = endpoint; 91 | } 92 | 93 | public async Task ListModelsAsync(CancellationToken cancellationToken) 94 | { 95 | var response = await Http.GetAsync(ModelsEndpoint, cancellationToken); 96 | if (!response.IsSuccessStatusCode) 97 | { 98 | var res = await response.Content.ReadAsStringAsync(); 99 | ErrorMsg = response.StatusCode.ToString() + res; 100 | return null; 101 | } 102 | 103 | var content = await response.Content.ReadAsStringAsync(); 104 | return JsonSerializer.Deserialize(content, JsonSerializerOptions); 105 | } 106 | 107 | /// 108 | /// chat 109 | /// 110 | /// 111 | public async Task ChatAsync(ChatRequest request, CancellationToken cancellationToken) 112 | { 113 | request.Stream = false; 114 | var content = new StringContent(JsonSerializer.Serialize(request, typeof(ChatRequest), JsonSerializerOptions), Encoding.UTF8, "application/json"); 115 | 116 | var response = await Http.PostAsync(ChatEndpoint, content, cancellationToken); 117 | if (!response.IsSuccessStatusCode) 118 | { 119 | var res = await response.Content.ReadAsStringAsync(); 120 | ErrorMsg = response.StatusCode.ToString() + res; 121 | return null; 122 | } 123 | var resContent = await response.Content.ReadAsStringAsync(); 124 | if (string.IsNullOrWhiteSpace(resContent)) 125 | { 126 | ErrorMsg = "empty response"; 127 | return null; 128 | } 129 | return JsonSerializer.Deserialize(resContent, JsonSerializerOptions); 130 | } 131 | 132 | /// 133 | /// streaming output 134 | /// 135 | /// 136 | /// 137 | /// 138 | /// 139 | public async IAsyncEnumerable? ChatStreamAsync(ChatRequest request, [EnumeratorCancellation] CancellationToken cancellationToken) 140 | { 141 | request.Stream = true; 142 | var content = new StringContent(JsonSerializer.Serialize(request, JsonSerializerOptions), Encoding.UTF8, "application/json"); 143 | 144 | var requestMessage = new HttpRequestMessage(HttpMethod.Post, ChatEndpoint) 145 | { 146 | Content = content, 147 | }; 148 | using var response = await Http.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); 149 | 150 | if (response.IsSuccessStatusCode) 151 | { 152 | var stream = await response.Content.ReadAsStreamAsync(cancellationToken); 153 | using var reader = new StreamReader(stream); 154 | 155 | while (!reader.EndOfStream && !cancellationToken.IsCancellationRequested) 156 | { 157 | var line = await reader.ReadLineAsync(cancellationToken); 158 | if (line != null && line.StartsWith("data: ")) 159 | { 160 | var json = line.Substring(6); 161 | if (!string.IsNullOrWhiteSpace(json) && json != StreamDoneSign) 162 | { 163 | var chatResponse = JsonSerializer.Deserialize(json, JsonSerializerOptions); 164 | var choice = chatResponse?.Choices.FirstOrDefault(); 165 | if (choice is null) 166 | { 167 | continue; 168 | } 169 | yield return choice; 170 | } 171 | } 172 | } 173 | } 174 | else 175 | { 176 | var res = await response.Content.ReadAsStringAsync(); 177 | ErrorMsg = response.StatusCode.ToString() + res; 178 | yield break; 179 | } 180 | } 181 | 182 | /// 183 | /// return raw response string 184 | /// 185 | /// 186 | /// 187 | /// 188 | public async IAsyncEnumerable? ChatStreamWithStringAsync(ChatRequest request, [EnumeratorCancellation] CancellationToken cancellationToken) 189 | { 190 | request.Stream = true; 191 | var content = new StringContent(JsonSerializer.Serialize(request, JsonSerializerOptions), Encoding.UTF8, "application/json"); 192 | 193 | var requestMessage = new HttpRequestMessage(HttpMethod.Post, ChatEndpoint) 194 | { 195 | Content = content, 196 | }; 197 | using var response = await Http.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); 198 | 199 | if (response.IsSuccessStatusCode) 200 | { 201 | var stream = await response.Content.ReadAsStreamAsync(cancellationToken); 202 | using var reader = new StreamReader(stream); 203 | 204 | while (!reader.EndOfStream && !cancellationToken.IsCancellationRequested) 205 | { 206 | var line = await reader.ReadLineAsync(cancellationToken); 207 | yield return line; 208 | } 209 | } 210 | else 211 | { 212 | var res = await response.Content.ReadAsStringAsync(); 213 | ErrorMsg = response.StatusCode.ToString() + res; 214 | yield break; 215 | } 216 | } 217 | 218 | /// 219 | /// Completions 220 | /// 221 | /// 222 | /// 223 | public async Task CompletionsAsync(CompletionRequest request, CancellationToken cancellationToken) 224 | { 225 | request.Stream = false; 226 | var content = new StringContent(JsonSerializer.Serialize(request, JsonSerializerOptions), Encoding.UTF8, "application/json"); 227 | 228 | string endpoint = CompletionEndpoint; 229 | if (Http.BaseAddress?.OriginalString == BaseAddress) 230 | { 231 | endpoint = "/beta" + CompletionEndpoint; 232 | } 233 | await Task.Delay(100); 234 | var response = await Http.PostAsync(endpoint, content, cancellationToken); 235 | if (!response.IsSuccessStatusCode) 236 | { 237 | var res = await response.Content.ReadAsStringAsync(); 238 | ErrorMsg = response.StatusCode.ToString() + res; 239 | return null; 240 | } 241 | var resContent = await response.Content.ReadAsStringAsync(); 242 | if (string.IsNullOrWhiteSpace(resContent)) 243 | { 244 | ErrorMsg = "empty response"; 245 | return null; 246 | } 247 | return JsonSerializer.Deserialize(resContent, JsonSerializerOptions); 248 | } 249 | 250 | /// 251 | /// Completions 252 | /// 253 | /// 254 | /// 255 | /// 256 | public async IAsyncEnumerable? CompletionsStreamAsync(CompletionRequest request, [EnumeratorCancellation] CancellationToken cancellationToken) 257 | { 258 | request.Stream = true; 259 | var content = new StringContent(JsonSerializer.Serialize(request, JsonSerializerOptions), Encoding.UTF8, "application/json"); 260 | string endpoint = CompletionEndpoint; 261 | if (Http.BaseAddress?.OriginalString == BaseAddress) 262 | { 263 | endpoint = "/beta" + CompletionEndpoint; 264 | } 265 | var requestMessage = new HttpRequestMessage(HttpMethod.Post, endpoint) 266 | { 267 | Content = content, 268 | }; 269 | 270 | var response = await Http.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); 271 | 272 | if (response.IsSuccessStatusCode) 273 | { 274 | var stream = await response.Content.ReadAsStreamAsync(cancellationToken); 275 | using var reader = new StreamReader(stream); 276 | 277 | while (!reader.EndOfStream && !cancellationToken.IsCancellationRequested) 278 | { 279 | var line = await reader.ReadLineAsync(); 280 | if (line != null && line.StartsWith("data: ")) 281 | { 282 | var json = line.Substring(6); 283 | if (!string.IsNullOrWhiteSpace(json) && json != StreamDoneSign) 284 | { 285 | var chatResponse = JsonSerializer.Deserialize(json, JsonSerializerOptions); 286 | var choice = chatResponse?.Choices.FirstOrDefault(); 287 | if (choice is null) 288 | { 289 | continue; 290 | } 291 | yield return choice; 292 | } 293 | } 294 | } 295 | } 296 | else 297 | { 298 | var res = await response.Content.ReadAsStringAsync(); 299 | ErrorMsg = response.StatusCode.ToString() + res; 300 | yield break; 301 | } 302 | } 303 | 304 | /// 305 | /// get user balance 306 | /// 307 | /// 308 | /// 309 | public async Task GetUserBalanceAsync(CancellationToken cancellationToken) 310 | { 311 | var response = await Http.GetAsync(UserBalanceEndpoint, cancellationToken); 312 | if (!response.IsSuccessStatusCode) 313 | { 314 | var res = await response.Content.ReadAsStringAsync(); 315 | ErrorMsg = response.StatusCode.ToString() + res; 316 | return null; 317 | } 318 | 319 | var content = await response.Content.ReadAsStringAsync(); 320 | if (string.IsNullOrWhiteSpace(content)) 321 | { 322 | ErrorMsg = "empty response"; 323 | return null; 324 | } 325 | return JsonSerializer.Deserialize(content, JsonSerializerOptions); 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/Models/ChatRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace DeepSeek.Core.Models; 5 | /// 6 | /// chat请求 7 | /// 8 | public class ChatRequest 9 | { 10 | /// 11 | /// 消息列表 12 | /// 13 | public List Messages { get; set; } = []; 14 | /// 15 | /// 使用的模型的 ID。您可以使用 deepseek-chat 或者 deepseek-reasoner。 16 | /// 17 | public string Model { get; set; } = DeepSeekModels.ChatModel; 18 | /// 19 | /// 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。 20 | /// 21 | [JsonPropertyName("frequency_penalty")] 22 | public double FrequencyPenalty { get; set; } = 0; 23 | /// 24 | /// 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。 25 | /// default:4096 26 | /// 27 | [JsonPropertyName("max_tokens")] 28 | public long MaxTokens { get; set; } = 4096; 29 | /// 30 | /// 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性。 31 | /// 32 | [JsonPropertyName("presence_penalty")] 33 | public double PresencePenalty { get; set; } = 0; 34 | 35 | /// 36 | /// type:text or json_object 37 | /// 38 | [JsonPropertyName("response_format")] 39 | public ResponseFormat? ResponseFormat { get; set; } 40 | 41 | /// 42 | /// Up to 16 sequences where the API will stop generating further tokens. 43 | /// 44 | public List Stop { get; set; } = []; 45 | /// 46 | /// 如果设置为 True,将会以 SSE(server-sent events)的形式以流式发送消息增量。消息流以 data: [DONE] 结尾。 47 | /// 48 | [JsonInclude] 49 | public bool Stream { get; set; } 50 | 51 | /// 52 | /// max 128 functions 53 | /// 54 | public List? Tools { get; set; } 55 | 56 | /// 57 | /// tool choice 58 | /// 59 | [JsonPropertyName("tool_choice")] 60 | public JsonElement? ToolChoice { get; set; } 61 | 62 | [JsonPropertyName("stream_options")] 63 | public StreamOptions? StreamOptions { get; set; } 64 | /// 65 | /// 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。 66 | public double Temperature { get; set; } = 1; 67 | /// 68 | /// 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。 69 | /// 70 | [JsonPropertyName("top_p")] 71 | public double TopP { get; set; } = 1; 72 | 73 | /// 74 | /// 是否返回所输出 token 的对数概率。如果为 true,则在 message 的 content 中返回每个输出 token 的对数概率 75 | /// 76 | [JsonPropertyName("logprobs")] 77 | public bool Logprobs { get; set; } 78 | /// 79 | /// 一个介于 0 到 20 之间的整数 N,指定每个输出位置返回输出概率 top N 的 token,且返回这些 token 的对数概率。指定此参数时,logprobs 必须为 true。 80 | /// 81 | [JsonPropertyName("top_logprobs")] 82 | public int? TopLogprobs { get; set; } 83 | } 84 | public class StreamOptions 85 | { 86 | [JsonPropertyName("include_usage")] 87 | public bool IncludeUsage { get; set; } 88 | } 89 | 90 | public class Tool 91 | { 92 | public string Type { get; set; } = "function"; 93 | public JsonElement Function { get; set; } 94 | } 95 | public class ResponseFormat 96 | { 97 | public string Type { get; set; } = ResponseFormatTypes.Text; 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/Models/ChatResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DeepSeek.Core.Models; 4 | /// 5 | /// chat response 6 | /// 7 | public class ChatResponse 8 | { 9 | /// 10 | /// 该对话的唯一标识符。 11 | /// 12 | public string Id { get; set; } = default!; 13 | /// 14 | /// 模型生成的 completion 的选择列表 15 | /// 16 | public List Choices { get; set; } = []; 17 | /// 18 | /// 创建聊天完成时的 Unix 时间戳(以秒为单位)。 19 | /// 20 | 21 | public long Created { get; set; } 22 | /// 23 | /// 生成该 completion 的模型名 24 | /// 25 | public string Model { get; set; } = default!; 26 | 27 | [JsonPropertyName("system_fingerprint")] 28 | public string? SystemFingerprint { get; set; } 29 | 30 | /// 31 | /// 对象的类型, 其值为 chat.completion 32 | /// 33 | public string Object { get; set; } = default!; 34 | /// 35 | /// 该对话补全请求的用量信息 36 | /// 37 | public Usage? Usage { get; set; } 38 | } 39 | 40 | /// 41 | /// 模型生成的选择 42 | /// 43 | public class Choice 44 | { 45 | [JsonPropertyName("finish_reason")] 46 | public string? FinishReason { get; set; } 47 | public int Index { get; set; } 48 | public Message? Message { get; set; } 49 | /// 50 | /// 该 choice 的对数概率信息。 51 | /// 52 | public Logprobs? Logprobs { get; set; } 53 | 54 | /// 55 | /// use this when streaming 56 | /// 57 | public Message? Delta { get; set; } 58 | 59 | /// 60 | /// for completion 61 | /// 62 | public string Text { get; set; } = string.Empty; 63 | } 64 | 65 | /// 66 | /// 对数概率信息 67 | /// 68 | public class Logprobs 69 | { 70 | [JsonPropertyName("text_offset")] 71 | public int[] TextOffset { get; set; } = []; 72 | 73 | public string[] Tokens { get; set; } = []; 74 | [JsonPropertyName("token_logprobs")] 75 | public double[] TokenLogProbs { get; set; } = []; 76 | 77 | [JsonPropertyName("top_logprobs")] 78 | public List? TopLogProbs { get; set; } 79 | 80 | /// 81 | /// 包含输出 token 对数概率信息的列表 82 | /// 83 | public List Content { get; set; } = []; 84 | } 85 | 86 | /// 87 | /// 对数概率信息 88 | /// 89 | public class Content 90 | { 91 | public string? Token { get; set; } 92 | public long Logprob { get; set; } 93 | public byte[] Bytes { get; set; } = []; 94 | [JsonPropertyName("top_logprobs")] 95 | public List TopLogprobs { get; set; } = []; 96 | } 97 | 98 | public class TopLogprobs 99 | { 100 | public string? Token { get; set; } 101 | public long Logprob { get; set; } 102 | public byte[] Bytes { get; set; } = []; 103 | } 104 | 105 | /// 106 | /// 用量信息 107 | /// 108 | public class Usage 109 | { 110 | [JsonPropertyName("completion_tokens")] 111 | public int CompletionTokens { get; set; } 112 | [JsonPropertyName("prompt_tokens")] 113 | public int PromptTokens { get; set; } 114 | 115 | [JsonPropertyName("prompt_cache_hit_tokens")] 116 | public int PromptCacheHitTokens { get; set; } 117 | [JsonPropertyName("prompt_cache_miss_tokens")] 118 | public int PromptCacheMissTokens { get; set; } 119 | [JsonPropertyName("total_tokens")] 120 | public int TotalTokens { get; set; } 121 | 122 | [JsonPropertyName("prompt_tokens_details")] 123 | public CompletionTokensDetails? Details { get; set; } 124 | 125 | public class CompletionTokensDetails 126 | { 127 | [JsonPropertyName("reasoning_tokens")] 128 | public int ReasoningTokens { get; set; } 129 | [JsonPropertyName("cached_tokens")] 130 | public int CachedTokens { get; set; } 131 | } 132 | } 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/Models/CompletionRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json.Serialization; 6 | using System.Text.Json; 7 | using System.Threading.Tasks; 8 | 9 | namespace DeepSeek.Core.Models; 10 | public class CompletionRequest 11 | { 12 | /// 13 | /// prompt 14 | /// 15 | public required string Prompt { get; set; } 16 | /// 17 | /// 使用的模型的 ID。您可以使用 deepseek-chat 或者 deepseek-reasoner。 18 | /// 19 | public string Model { get; set; } = DeepSeekModels.ChatModel; 20 | 21 | /// 22 | /// output prompt 23 | /// 24 | public bool? Echo { get; set; } 25 | 26 | /// 27 | /// 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。 28 | /// 29 | [JsonPropertyName("frequency_penalty")] 30 | public double FrequencyPenalty { get; set; } = 0; 31 | /// 32 | /// 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。 33 | /// default:4096 34 | /// 35 | [JsonPropertyName("max_tokens")] 36 | public long MaxTokens { get; set; } = 4096; 37 | /// 38 | /// 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性。 39 | /// 40 | [JsonPropertyName("presence_penalty")] 41 | public double PresencePenalty { get; set; } = 0; 42 | 43 | /// 44 | /// Up to 16 sequences where the API will stop generating further tokens. 45 | /// 46 | public List? Stop { get; set; } 47 | /// 48 | /// 如果设置为 True,将会以 SSE(server-sent events)的形式以流式发送消息增量。消息流以 data: [DONE] 结尾。 49 | /// 50 | [JsonInclude] 51 | public bool Stream { get; set; } 52 | 53 | /// 54 | /// suffix 55 | /// 56 | public string? Suffix { get; set; } 57 | 58 | [JsonPropertyName("stream_options")] 59 | public StreamOptions? StreamOptions { get; set; } 60 | /// 61 | /// 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。 62 | public double Temperature { get; set; } = 1; 63 | /// 64 | /// 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。 65 | /// 66 | [JsonPropertyName("top_p")] 67 | public long TopP { get; set; } = 1; 68 | 69 | /// 70 | /// <=20 71 | /// 72 | [JsonPropertyName("logprobs")] 73 | public int? Logprobs { get; set; } = 0; 74 | } 75 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/Models/Message.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DeepSeek.Core.Models; 4 | public class Message 5 | { 6 | public string Content { get; set; } = string.Empty; 7 | public string Role { get; set; } = "user"; 8 | 9 | public string? Name { get; set; } 10 | 11 | /// 12 | /// beta feature 13 | /// 14 | public bool? Prefix { get; set; } 15 | 16 | /// 17 | /// beta feature 18 | /// 19 | [JsonPropertyName("reasoning_content")] 20 | public string? ReasoningContent { get; set; } 21 | 22 | [JsonPropertyName("tool_call_id")] 23 | public string? ToolCallId { get; set; } 24 | 25 | public static Message NewUserMessage(string content) 26 | { 27 | return new Message 28 | { 29 | Content = content, 30 | Role = "user" 31 | }; 32 | } 33 | 34 | public static Message NewSystemMessage(string content) 35 | { 36 | return new Message 37 | { 38 | Content = content, 39 | Role = "system" 40 | }; 41 | } 42 | 43 | public static Message NewAssistantMessage(string content, bool? prefix = false, string? reasoningContent = null) 44 | { 45 | return new Message 46 | { 47 | Content = content, 48 | Role = "assistant", 49 | Prefix = prefix, 50 | ReasoningContent = reasoningContent 51 | }; 52 | } 53 | 54 | public static Message NewToolMessage(string content, string toolCallId) 55 | { 56 | return new Message 57 | { 58 | Content = content, 59 | Role = "tool", 60 | ToolCallId = toolCallId 61 | }; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/Models/ModelResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DeepSeek.Core.Models; 4 | 5 | public class ModelResponse 6 | { 7 | public string Object { get; set; } = string.Empty; 8 | public List Data { get; set; } = []; 9 | } 10 | 11 | public record Model 12 | { 13 | public string? Id { get; set; } 14 | public string? Object { get; set; } 15 | [JsonPropertyName("owned_by")] 16 | public string? OwnedBy { get; set; } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/Models/SourceGenerationContext.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DeepSeek.Core.Models; 4 | 5 | 6 | [JsonSerializable(typeof(ChatRequest))] 7 | [JsonSerializable(typeof(ChatResponse))] 8 | [JsonSerializable(typeof(CompletionRequest))] 9 | [JsonSerializable(typeof(Message))] 10 | [JsonSerializable(typeof(ModelResponse))] 11 | [JsonSerializable(typeof(UserResponse))] 12 | [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] 13 | public partial class SourceGenerationContext : JsonSerializerContext 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /src/DeepSeek.Core/Models/UserResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json.Serialization; 6 | using System.Threading.Tasks; 7 | 8 | namespace DeepSeek.Core.Models; 9 | public class UserResponse 10 | { 11 | /// 12 | /// has balance 13 | /// 14 | [JsonPropertyName("is_available")] 15 | public bool IsAvailable { get; set; } 16 | 17 | [JsonPropertyName("balance_infos")] 18 | public List BalanceInfos { get; set; } = []; 19 | } 20 | 21 | public class UserBalance 22 | { 23 | /// 24 | /// CNY or USD 25 | /// 26 | public string Currency { get; set; } = "CNY"; 27 | 28 | [JsonPropertyName("total_balance")] 29 | public string TotalBalance { get; set; } = string.Empty; 30 | [JsonPropertyName("granted_balance")] 31 | public string GrantedBalance { get; set; } = string.Empty; 32 | 33 | [JsonPropertyName("topped_up_balance")] 34 | public string ToppedUpBalance { get; set; } = string.Empty; 35 | } 36 | --------------------------------------------------------------------------------