Skip to content

Commit 18a9b67

Browse files
authored
Merge pull request #7 from otapiero/main
Immprovment for package and real support for labels
2 parents c80cd33 + c29edf1 commit 18a9b67

21 files changed

+990
-693
lines changed

DependencyInjection.cs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,17 @@ public static IServiceCollection AddPromptProvider(
1313
Action<PromptsOptions>? configurePrompts = null,
1414
Action<PromptKeyOptions>? configurePromptKeys = null)
1515
{
16-
if (configureLangfuse is not null) services.Configure(configureLangfuse);
16+
var langfuseBuilder = services.AddOptions<LangfuseOptions>();
17+
if (configureLangfuse is not null)
18+
{
19+
langfuseBuilder.Configure(configureLangfuse);
20+
}
21+
22+
langfuseBuilder
23+
.Validate(IsLangfuseConfigValid,
24+
"Langfuse configuration is invalid. Provide all of BaseUrl/PublicKey/SecretKey, and ensure BaseUrl is an absolute URI, or leave all blank.")
25+
.ValidateOnStart();
26+
1727
if (configurePrompts is not null) services.Configure(configurePrompts);
1828
if (configurePromptKeys is not null) services.Configure(configurePromptKeys);
1929

@@ -24,12 +34,47 @@ public static IServiceCollection AddPromptProvider(
2434
{
2535
client.BaseAddress = new Uri(options.BaseUrl);
2636
}
37+
38+
if (options.HttpClient.RequestTimeoutSeconds is > 0)
39+
{
40+
client.Timeout = TimeSpan.FromSeconds(options.HttpClient.RequestTimeoutSeconds.Value);
41+
}
42+
})
43+
.ConfigurePrimaryHttpMessageHandler(serviceProvider =>
44+
{
45+
var options = serviceProvider.GetRequiredService<Microsoft.Extensions.Options.IOptions<LangfuseOptions>>().Value;
46+
return new SocketsHttpHandler
47+
{
48+
AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate,
49+
MaxConnectionsPerServer = options.HttpClient.MaxConnectionsPerServer.GetValueOrDefault(int.MaxValue),
50+
PooledConnectionLifetime = options.HttpClient.PooledConnectionLifetimeMinutes is > 0
51+
? TimeSpan.FromMinutes(options.HttpClient.PooledConnectionLifetimeMinutes.Value)
52+
: Timeout.InfiniteTimeSpan
53+
};
2754
});
28-
services.AddScoped<IPromptService, PromptService>();
2955

30-
// Register default prompts provider (users may replace with custom implementation)
56+
services.AddScoped<IPromptService, PromptService>();
3157
services.AddSingleton<IDefaultPromptsProvider, ConfigurationDefaultPromptsProvider>();
3258

3359
return services;
3460
}
61+
62+
private static bool IsLangfuseConfigValid(LangfuseOptions options)
63+
{
64+
var hasAny = !string.IsNullOrWhiteSpace(options.BaseUrl)
65+
|| !string.IsNullOrWhiteSpace(options.PublicKey)
66+
|| !string.IsNullOrWhiteSpace(options.SecretKey);
67+
68+
if (!hasAny)
69+
{
70+
return true;
71+
}
72+
73+
if (!options.IsConfigured())
74+
{
75+
return false;
76+
}
77+
78+
return Uri.TryCreate(options.BaseUrl, UriKind.Absolute, out _);
79+
}
3580
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
namespace PromptProvider.Interfaces;
22

33
using PromptProvider.Models;
4-
using PromptProvider.Options;
54

65
public interface IDefaultPromptsProvider
76
{
@@ -10,4 +9,8 @@ public interface IDefaultPromptsProvider
109
IReadOnlyDictionary<string, ChatMessage[]> GetChatDefaults();
1110

1211
IReadOnlyDictionary<string, PromptConfiguration> GetPromptKeys();
12+
13+
IReadOnlyDictionary<string, ResolvedPromptConfiguration> GetResolvedPrompts();
14+
15+
bool TryGetResolvedPrompt(string logicalKey, out ResolvedPromptConfiguration configuration);
1316
}

Interfaces/ILangfuseService.cs

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,69 +4,28 @@ namespace PromptProvider.Interfaces;
44

55
public interface ILangfuseService
66
{
7-
/// <summary>
8-
/// Get a prompt by name from Langfuse API.
9-
/// </summary>
10-
/// <param name="promptName">The name of the prompt</param>
11-
/// <param name="version">Optional version of the prompt to retrieve</param>
12-
/// <param name="label">Optional label of the prompt (defaults to "production" if no label or version is set)</param>
13-
/// <param name="cancellationToken">Cancellation token</param>
14-
/// <returns>The Langfuse prompt model</returns>
157
Task<LangfusePromptModel?> GetPromptAsync(
168
string promptName,
179
int? version = null,
1810
string? label = null,
1911
CancellationToken cancellationToken = default);
2012

21-
/// <summary>
22-
/// Get a chat prompt by name from Langfuse API.
23-
/// </summary>
24-
/// <param name="promptName">The name of the chat prompt</param>
25-
/// <param name="version">Optional version of the prompt to retrieve</param>
26-
/// <param name="label">Optional label of the prompt (defaults to "production" if no label or version is set)</param>
27-
/// <param name="cancellationToken">Cancellation token</param>
28-
/// <returns>The Langfuse chat prompt model</returns>
2913
Task<LangfuseChatPromptModel?> GetChatPromptAsync(
3014
string promptName,
3115
int? version = null,
3216
string? label = null,
3317
CancellationToken cancellationToken = default);
3418

35-
/// <summary>
36-
/// Get all prompts from Langfuse API.
37-
/// </summary>
38-
/// <param name="cancellationToken">Cancellation token</param>
39-
/// <returns>List of Langfuse prompt list items</returns>
40-
Task<List<LangfusePromptListItem>> GetAllPromptsAsync(CancellationToken cancellationToken = default);
19+
Task<IReadOnlyList<LangfusePromptListItem>> GetAllPromptsAsync(CancellationToken cancellationToken = default);
4120

42-
/// <summary>
43-
/// Create a new version for the prompt with the given name in Langfuse API.
44-
/// </summary>
45-
/// <param name="request">The prompt creation request</param>
46-
/// <param name="cancellationToken">Cancellation token</param>
47-
/// <returns>The created prompt response</returns>
4821
Task<CreateLangfusePromptResponse> CreatePromptAsync(
4922
CreateLangfusePromptRequest request,
5023
CancellationToken cancellationToken = default);
5124

52-
/// <summary>
53-
/// Create a new version for the chat prompt with the given name in Langfuse API.
54-
/// </summary>
55-
/// <param name="request">The chat prompt creation request</param>
56-
/// <param name="cancellationToken">Cancellation token</param>
57-
/// <returns>The created chat prompt response</returns>
5825
Task<CreateLangfuseChatPromptResponse> CreateChatPromptAsync(
5926
CreateLangfuseChatPromptRequest request,
6027
CancellationToken cancellationToken = default);
6128

62-
/// <summary>
63-
/// Update labels for a specific prompt version in Langfuse API.
64-
/// </summary>
65-
/// <param name="promptName">The name of the prompt</param>
66-
/// <param name="version">The version number to update</param>
67-
/// <param name="request">The update request containing new labels</param>
68-
/// <param name="cancellationToken">Cancellation token</param>
69-
/// <returns>The updated prompt model</returns>
7029
Task<LangfusePromptModel> UpdatePromptLabelsAsync(
7130
string promptName,
7231
int version,

Interfaces/IPromptService.cs

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,38 @@ namespace PromptProvider.Interfaces;
44

55
public interface IPromptService
66
{
7-
/// <summary>
8-
/// Create a new prompt version in Langfuse
9-
/// </summary>
107
Task<PromptResponse> CreatePromptAsync(CreatePromptRequest request, CancellationToken cancellationToken = default);
118

12-
/// <summary>
13-
/// Create a new chat prompt version in Langfuse
14-
/// </summary>
159
Task<ChatPromptResponse> CreateChatPromptAsync(CreateChatPromptRequest request, CancellationToken cancellationToken = default);
1610

1711
/// <summary>
18-
/// Get a prompt by key with optional version or label. Falls back to local defaults if Langfuse is unavailable.
12+
/// Retrieves a text prompt.
13+
/// Precedence rules:
14+
/// 1) explicit method parameters (version/label)
15+
/// 2) configured prompt entry defaults
16+
/// 3) Langfuse default behavior.
17+
/// If a version is effective, label is ignored.
18+
/// Falls back to local configured defaults when remote retrieval fails.
1919
/// </summary>
20-
/// <param name="promptKey">The prompt key/name</param>
21-
/// <param name="version">Optional specific version number</param>
22-
/// <param name="label">Optional label (e.g., "production", "latest")</param>
23-
/// <param name="cancellationToken">Cancellation token</param>
2420
Task<PromptResponse?> GetPromptAsync(string promptKey, int? version = null, string? label = null, CancellationToken cancellationToken = default);
2521

2622
/// <summary>
27-
/// Get a chat prompt by key with optional version or label. Falls back to local defaults if Langfuse is unavailable.
23+
/// Retrieves a chat prompt.
24+
/// Precedence rules:
25+
/// 1) explicit method parameters (version/label)
26+
/// 2) configured prompt entry defaults
27+
/// 3) Langfuse default behavior.
28+
/// If a version is effective, label is ignored.
29+
/// Falls back to local configured defaults when remote retrieval fails.
2830
/// </summary>
29-
/// <param name="promptKey">The prompt key/name</param>
30-
/// <param name="version">Optional specific version number</param>
31-
/// <param name="label">Optional label (e.g., "production", "latest")</param>
32-
/// <param name="cancellationToken">Cancellation token</param>
3331
Task<ChatPromptResponse?> GetChatPromptAsync(string promptKey, int? version = null, string? label = null, CancellationToken cancellationToken = default);
3432

35-
/// <summary>
36-
/// Get all prompts from Langfuse
37-
/// </summary>
38-
Task<List<LangfusePromptListItem>> GetAllPromptsAsync(CancellationToken cancellationToken = default);
33+
Task<IReadOnlyList<LangfusePromptListItem>> GetAllPromptsAsync(CancellationToken cancellationToken = default);
3934

4035
/// <summary>
41-
/// Get multiple prompts by keys with optional label. Falls back to local defaults if Langfuse is unavailable.
36+
/// Retrieves prompts by keys. Individual key failures do not fail the whole batch.
4237
/// </summary>
43-
Task<List<PromptResponse>> GetPromptsAsync(IEnumerable<string> promptKeys, string? label = null, CancellationToken cancellationToken = default);
38+
Task<IReadOnlyList<PromptResponse>> GetPromptsAsync(IEnumerable<string> promptKeys, string? label = null, CancellationToken cancellationToken = default);
4439

45-
/// <summary>
46-
/// Update labels for a specific prompt version in Langfuse
47-
/// </summary>
4840
Task<PromptResponse> UpdatePromptLabelsAsync(string promptKey, int version, UpdatePromptLabelsRequest request, CancellationToken cancellationToken = default);
4941
}

Models/ChatPromptResponse.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Text.Json.Serialization;
2+
13
namespace PromptProvider.Models;
24

35
public record ChatPromptResponse
@@ -7,7 +9,9 @@ public record ChatPromptResponse
79
public int? Version { get; set; }
810
public string[]? Labels { get; set; }
911
public string[]? Tags { get; set; }
10-
public string? Type { get; set; }
12+
[JsonConverter(typeof(JsonStringEnumConverter))]
13+
public PromptKind Type { get; set; } = PromptKind.Chat;
1114
public LangfusePromptConfiguration? Config { get; set; }
12-
public string? Source { get; set; } // "Langfuse" or "Local"
15+
[JsonConverter(typeof(JsonStringEnumConverter))]
16+
public PromptSource Source { get; set; }
1317
}

Models/GetPromptsBatchRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ namespace PromptProvider.Models;
22

33
public sealed record GetPromptsBatchRequest
44
{
5-
public required List<PromptConfiguration> Prompts { get; init; }
5+
public required IReadOnlyList<PromptConfiguration> Prompts { get; init; }
66
}

Models/GetPromptsBatchResponse.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ namespace PromptProvider.Models;
22

33
public sealed record GetPromptsBatchResponse
44
{
5-
public required List<PromptResponse> Prompts { get; init; }
6-
public required List<string> NotFound { get; init; }
7-
}
5+
public required IReadOnlyList<PromptResponse> Prompts { get; init; }
6+
public required IReadOnlyList<string> NotFound { get; init; }
7+
}

Models/PromptConfiguration.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ namespace PromptProvider.Models;
33
public sealed class PromptConfiguration
44
{
55
/// <summary>
6-
/// The prompt key/name
6+
/// The Langfuse prompt key/name.
77
/// </summary>
88
public required string Key { get; set; }
99

1010
/// <summary>
11-
/// Optional specific version number to use
11+
/// Optional default version used when method argument version is not provided.
12+
/// If effective version exists, effective label is ignored.
1213
/// </summary>
1314
public int? Version { get; set; }
1415

1516
/// <summary>
16-
/// Optional label to use (e.g., "production", "staging", "latest")
17-
/// If neither version nor label is specified, defaults to "production"
17+
/// Optional default label used when method argument label is not provided.
18+
/// Ignored when effective version exists.
1819
/// </summary>
1920
public string? Label { get; set; }
2021
}

Models/PromptEnums.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace PromptProvider.Models;
2+
3+
public enum PromptSource
4+
{
5+
Local = 0,
6+
Langfuse = 1
7+
}
8+
9+
public enum PromptKind
10+
{
11+
Text = 0,
12+
Chat = 1,
13+
Unknown = 2
14+
}

Models/PromptResponse.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace PromptProvider.Models;
1+
using System.Text.Json.Serialization;
2+
3+
namespace PromptProvider.Models;
24

35
public record PromptResponse
46
{
@@ -7,7 +9,9 @@ public record PromptResponse
79
public int? Version { get; set; }
810
public string[]? Labels { get; set; }
911
public string[]? Tags { get; set; }
10-
public string? Type { get; set; }
12+
[JsonConverter(typeof(JsonStringEnumConverter))]
13+
public PromptKind Type { get; set; } = PromptKind.Text;
1114
public LangfusePromptConfiguration? Config { get; set; }
12-
public string? Source { get; set; } // "Langfuse" or "Local"
15+
[JsonConverter(typeof(JsonStringEnumConverter))]
16+
public PromptSource Source { get; set; }
1317
}

0 commit comments

Comments
 (0)