💄 Optimize the AI agent experience
This commit is contained in:
@@ -79,7 +79,7 @@ public class DeveloperController(
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var pubResponse = await ps.GetPublisherAsync(new GetPublisherRequest { Name = name });
|
var pubResponse = await ps.GetPublisherAsync(new GetPublisherRequest { Name = name });
|
||||||
pub = SnPublisher.FromProto(pubResponse.Publisher);
|
pub = SnPublisher.FromProtoValue(pubResponse.Publisher);
|
||||||
} catch (RpcException ex)
|
} catch (RpcException ex)
|
||||||
{
|
{
|
||||||
return NotFound(ex.Status.Detail);
|
return NotFound(ex.Status.Detail);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class DeveloperService(
|
|||||||
public async Task<SnDeveloper> LoadDeveloperPublisher(SnDeveloper developer)
|
public async Task<SnDeveloper> LoadDeveloperPublisher(SnDeveloper developer)
|
||||||
{
|
{
|
||||||
var pubResponse = await ps.GetPublisherAsync(new GetPublisherRequest { Id = developer.PublisherId.ToString() });
|
var pubResponse = await ps.GetPublisherAsync(new GetPublisherRequest { Id = developer.PublisherId.ToString() });
|
||||||
developer.Publisher = SnPublisher.FromProto(pubResponse.Publisher);
|
developer.Publisher = SnPublisher.FromProtoValue(pubResponse.Publisher);
|
||||||
return developer;
|
return developer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ public class DeveloperService(
|
|||||||
var pubRequest = new GetPublisherBatchRequest();
|
var pubRequest = new GetPublisherBatchRequest();
|
||||||
pubIds.ForEach(x => pubRequest.Ids.Add(x.ToString()));
|
pubIds.ForEach(x => pubRequest.Ids.Add(x.ToString()));
|
||||||
var pubResponse = await ps.GetPublisherBatchAsync(pubRequest);
|
var pubResponse = await ps.GetPublisherBatchAsync(pubRequest);
|
||||||
var pubs = pubResponse.Publishers.ToDictionary(p => Guid.Parse(p.Id), SnPublisher.FromProto);
|
var pubs = pubResponse.Publishers.ToDictionary(p => Guid.Parse(p.Id), SnPublisher.FromProtoValue);
|
||||||
|
|
||||||
return enumerable.Select(d =>
|
return enumerable.Select(d =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,13 +26,14 @@ public class ThoughtController(ThoughtProvider provider, ThoughtService service)
|
|||||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
|
|
||||||
// Generate topic if creating new sequence
|
// Generate a topic if creating a new sequence
|
||||||
string? topic = null;
|
string? topic = null;
|
||||||
if (!request.SequenceId.HasValue)
|
if (!request.SequenceId.HasValue)
|
||||||
{
|
{
|
||||||
// Use AI to summarize topic from user message
|
// Use AI to summarize a topic from a user message
|
||||||
var summaryHistory = new ChatHistory(
|
var summaryHistory = new ChatHistory(
|
||||||
"You are a helpful assistant. Summarize the following user message into a concise topic title (max 100 characters). Direct give the topic you summerized, do not add extra preifx / suffix."
|
"You are a helpful assistant. Summarize the following user message into a concise topic title (max 100 characters).\n" +
|
||||||
|
"Direct give the topic you summerized, do not add extra prefix / suffix."
|
||||||
);
|
);
|
||||||
summaryHistory.AddUserMessage(request.UserMessage);
|
summaryHistory.AddUserMessage(request.UserMessage);
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ public class ThoughtController(ThoughtProvider provider, ThoughtService service)
|
|||||||
))
|
))
|
||||||
{
|
{
|
||||||
// Write each chunk to the HTTP response as SSE
|
// Write each chunk to the HTTP response as SSE
|
||||||
var data = chunk.Content ?? "";
|
var data = chunk.ToString();
|
||||||
accumulatedContent.Append(data);
|
accumulatedContent.Append(data);
|
||||||
if (string.IsNullOrEmpty(data)) continue;
|
if (string.IsNullOrEmpty(data)) continue;
|
||||||
|
|
||||||
@@ -121,14 +122,15 @@ public class ThoughtController(ThoughtProvider provider, ThoughtService service)
|
|||||||
// Write the topic if it was newly set, then the thought object as JSON to the stream
|
// Write the topic if it was newly set, then the thought object as JSON to the stream
|
||||||
using (var streamBuilder = new MemoryStream())
|
using (var streamBuilder = new MemoryStream())
|
||||||
{
|
{
|
||||||
await streamBuilder.WriteAsync("\n"u8.ToArray());
|
await streamBuilder.WriteAsync("\n\ndata: "u8.ToArray());
|
||||||
if (topic != null)
|
if (topic != null)
|
||||||
{
|
{
|
||||||
await streamBuilder.WriteAsync(Encoding.UTF8.GetBytes($"<topic>{sequence.Topic ?? ""}</topic>\n"));
|
var topicJson = JsonSerializer.Serialize(new { type = "topic", data = sequence.Topic ?? "" });
|
||||||
|
await streamBuilder.WriteAsync(Encoding.UTF8.GetBytes($"{topicJson}\n\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
await streamBuilder.WriteAsync(
|
var thoughtJson = JsonSerializer.Serialize(new { type = "thought", data = savedThought }, GrpcTypeHelper.SerializerOptions);
|
||||||
Encoding.UTF8.GetBytes(JsonSerializer.Serialize(savedThought, GrpcTypeHelper.SerializerOptions)));
|
await streamBuilder.WriteAsync(Encoding.UTF8.GetBytes($"data: {thoughtJson}\n\n"));
|
||||||
var outputBytes = streamBuilder.ToArray();
|
var outputBytes = streamBuilder.ToArray();
|
||||||
await Response.Body.WriteAsync(outputBytes);
|
await Response.Body.WriteAsync(outputBytes);
|
||||||
await Response.Body.FlushAsync();
|
await Response.Body.FlushAsync();
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
using System.ClientModel;
|
using System.ClientModel;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using DysonNetwork.Shared.Models;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
using Microsoft.SemanticKernel;
|
using Microsoft.SemanticKernel;
|
||||||
using Microsoft.SemanticKernel.Connectors.Ollama;
|
using Microsoft.SemanticKernel.Connectors.Ollama;
|
||||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||||
using OpenAI;
|
using OpenAI;
|
||||||
|
using PostPinMode = DysonNetwork.Shared.Proto.PostPinMode;
|
||||||
|
using PostType = DysonNetwork.Shared.Proto.PostType;
|
||||||
|
|
||||||
namespace DysonNetwork.Insight.Thought;
|
namespace DysonNetwork.Insight.Thought;
|
||||||
|
|
||||||
public class ThoughtProvider
|
public class ThoughtProvider
|
||||||
{
|
{
|
||||||
private readonly Kernel _kernel;
|
|
||||||
private readonly PostService.PostServiceClient _postClient;
|
private readonly PostService.PostServiceClient _postClient;
|
||||||
private readonly AccountService.AccountServiceClient _accountClient;
|
private readonly AccountService.AccountServiceClient _accountClient;
|
||||||
|
|
||||||
public Kernel Kernel => _kernel;
|
public Kernel Kernel { get; }
|
||||||
public string? ModelProviderType { get; private set; }
|
|
||||||
public string? ModelDefault { get; private set; }
|
private string? ModelProviderType { get; set; }
|
||||||
|
private string? ModelDefault { get; set; }
|
||||||
|
|
||||||
public ThoughtProvider(
|
public ThoughtProvider(
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
@@ -27,7 +30,7 @@ public class ThoughtProvider
|
|||||||
_postClient = postClient;
|
_postClient = postClient;
|
||||||
_accountClient = accountClient;
|
_accountClient = accountClient;
|
||||||
|
|
||||||
_kernel = InitializeThinkingProvider(configuration);
|
Kernel = InitializeThinkingProvider(configuration);
|
||||||
InitializeHelperFunctions();
|
InitializeHelperFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ public class ThoughtProvider
|
|||||||
private void InitializeHelperFunctions()
|
private void InitializeHelperFunctions()
|
||||||
{
|
{
|
||||||
// Add Solar Network tools plugin
|
// Add Solar Network tools plugin
|
||||||
_kernel.ImportPluginFromFunctions("helper_functions", [
|
Kernel.ImportPluginFromFunctions("helper_functions", [
|
||||||
KernelFunctionFactory.CreateFromMethod(async (string userId) =>
|
KernelFunctionFactory.CreateFromMethod(async (string userId) =>
|
||||||
{
|
{
|
||||||
var request = new GetAccountRequest { Id = userId };
|
var request = new GetAccountRequest { Id = userId };
|
||||||
@@ -131,7 +134,7 @@ public class ThoughtProvider
|
|||||||
if (tags != null) request.Tags.AddRange(tags);
|
if (tags != null) request.Tags.AddRange(tags);
|
||||||
if (types != null) request.Types_.AddRange(types.Select(t => (PostType)t));
|
if (types != null) request.Types_.AddRange(types.Select(t => (PostType)t));
|
||||||
var response = await _postClient.ListPostsAsync(request);
|
var response = await _postClient.ListPostsAsync(request);
|
||||||
return JsonSerializer.Serialize(response.Posts, GrpcTypeHelper.SerializerOptions);
|
return JsonSerializer.Serialize(response.Posts.Select(SnPost.FromProtoValue), GrpcTypeHelper.SerializerOptions);
|
||||||
}, "list_posts", "Get posts from the Solar Network with customizable filters.")
|
}, "list_posts", "Get posts from the Solar Network with customizable filters.")
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,13 +100,13 @@ public class SnPost : ModelBase, IIdentifiedResource, IActivity
|
|||||||
Upvotes = Upvotes,
|
Upvotes = Upvotes,
|
||||||
Downvotes = Downvotes,
|
Downvotes = Downvotes,
|
||||||
AwardedScore = (double)AwardedScore,
|
AwardedScore = (double)AwardedScore,
|
||||||
ReactionsCount = { ReactionsCount ?? new Dictionary<string, int>() },
|
ReactionsCount = { ReactionsCount },
|
||||||
RepliesCount = RepliesCount,
|
RepliesCount = RepliesCount,
|
||||||
ReactionsMade = { ReactionsMade ?? new Dictionary<string, bool>() },
|
ReactionsMade = { ReactionsMade ?? new Dictionary<string, bool>() },
|
||||||
RepliedGone = RepliedGone,
|
RepliedGone = RepliedGone,
|
||||||
ForwardedGone = ForwardedGone,
|
ForwardedGone = ForwardedGone,
|
||||||
PublisherId = PublisherId.ToString(),
|
PublisherId = PublisherId.ToString(),
|
||||||
Publisher = Publisher.ToProto(),
|
Publisher = Publisher.ToProtoValue(),
|
||||||
CreatedAt = Timestamp.FromDateTimeOffset(CreatedAt.ToDateTimeOffset()),
|
CreatedAt = Timestamp.FromDateTimeOffset(CreatedAt.ToDateTimeOffset()),
|
||||||
UpdatedAt = Timestamp.FromDateTimeOffset(UpdatedAt.ToDateTimeOffset())
|
UpdatedAt = Timestamp.FromDateTimeOffset(UpdatedAt.ToDateTimeOffset())
|
||||||
};
|
};
|
||||||
@@ -128,7 +128,7 @@ public class SnPost : ModelBase, IIdentifiedResource, IActivity
|
|||||||
|
|
||||||
if (PinMode.HasValue)
|
if (PinMode.HasValue)
|
||||||
{
|
{
|
||||||
proto.PinMode = (Proto.PostPinMode)((int)PinMode.Value);
|
proto.PinMode = (Proto.PostPinMode)((int)PinMode.Value + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Meta != null)
|
if (Meta != null)
|
||||||
@@ -186,6 +186,95 @@ public class SnPost : ModelBase, IIdentifiedResource, IActivity
|
|||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SnPost FromProtoValue(Post proto)
|
||||||
|
{
|
||||||
|
var post = new SnPost
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
Title = string.IsNullOrEmpty(proto.Title) ? null : proto.Title,
|
||||||
|
Description = string.IsNullOrEmpty(proto.Description) ? null : proto.Description,
|
||||||
|
Slug = string.IsNullOrEmpty(proto.Slug) ? null : proto.Slug,
|
||||||
|
Visibility = (PostVisibility)((int)proto.Visibility - 1),
|
||||||
|
Type = (PostType)((int)proto.Type - 1),
|
||||||
|
ViewsUnique = proto.ViewsUnique,
|
||||||
|
ViewsTotal = proto.ViewsTotal,
|
||||||
|
Upvotes = proto.Upvotes,
|
||||||
|
Downvotes = proto.Downvotes,
|
||||||
|
AwardedScore = (decimal)proto.AwardedScore,
|
||||||
|
ReactionsCount = proto.ReactionsCount.ToDictionary(kv => kv.Key, kv => kv.Value),
|
||||||
|
RepliesCount = proto.RepliesCount,
|
||||||
|
ReactionsMade = proto.ReactionsMade.ToDictionary(kv => kv.Key, kv => kv.Value),
|
||||||
|
RepliedGone = proto.RepliedGone,
|
||||||
|
ForwardedGone = proto.ForwardedGone,
|
||||||
|
PublisherId = Guid.Parse(proto.PublisherId),
|
||||||
|
Publisher = SnPublisher.FromProtoValue(proto.Publisher),
|
||||||
|
CreatedAt = Instant.FromDateTimeOffset(proto.CreatedAt.ToDateTimeOffset()),
|
||||||
|
UpdatedAt = Instant.FromDateTimeOffset(proto.UpdatedAt.ToDateTimeOffset())
|
||||||
|
};
|
||||||
|
|
||||||
|
if (proto.EditedAt is not null)
|
||||||
|
post.EditedAt = Instant.FromDateTimeOffset(proto.EditedAt.ToDateTimeOffset());
|
||||||
|
|
||||||
|
if (proto.PublishedAt is not null)
|
||||||
|
post.PublishedAt = Instant.FromDateTimeOffset(proto.PublishedAt.ToDateTimeOffset());
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(proto.Content))
|
||||||
|
post.Content = proto.Content;
|
||||||
|
|
||||||
|
if (proto is { HasPinMode: true, PinMode: > 0 })
|
||||||
|
post.PinMode = (PostPinMode)(proto.PinMode - 1);
|
||||||
|
|
||||||
|
if (proto.Meta != null)
|
||||||
|
post.Meta = GrpcTypeHelper.ConvertByteStringToObject<Dictionary<string, object>>(proto.Meta);
|
||||||
|
|
||||||
|
if (proto.SensitiveMarks != null)
|
||||||
|
post.SensitiveMarks =
|
||||||
|
GrpcTypeHelper.ConvertByteStringToObject<List<ContentSensitiveMark>>(proto.SensitiveMarks);
|
||||||
|
|
||||||
|
if (proto.EmbedView is not null)
|
||||||
|
post.EmbedView = PostEmbedView.FromProtoValue(proto.EmbedView);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(proto.RepliedPostId))
|
||||||
|
{
|
||||||
|
post.RepliedPostId = Guid.Parse(proto.RepliedPostId);
|
||||||
|
if (proto.RepliedPost is not null)
|
||||||
|
post.RepliedPost = FromProtoValue(proto.RepliedPost);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(proto.ForwardedPostId))
|
||||||
|
{
|
||||||
|
post.ForwardedPostId = Guid.Parse(proto.ForwardedPostId);
|
||||||
|
if (proto.ForwardedPost is not null)
|
||||||
|
post.ForwardedPost = FromProtoValue(proto.ForwardedPost);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(proto.RealmId))
|
||||||
|
{
|
||||||
|
post.RealmId = Guid.Parse(proto.RealmId);
|
||||||
|
if (proto.Realm is not null)
|
||||||
|
post.Realm = SnRealm.FromProtoValue(proto.Realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
post.Attachments.AddRange(proto.Attachments.Select(SnCloudFileReferenceObject.FromProtoValue));
|
||||||
|
post.Awards.AddRange(proto.Awards.Select(a => new SnPostAward
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(a.Id), PostId = Guid.Parse(a.PostId), AccountId = Guid.Parse(a.AccountId),
|
||||||
|
Amount = (decimal)a.Amount, Attitude = (PostReactionAttitude)((int)a.Attitude - 1),
|
||||||
|
Message = string.IsNullOrEmpty(a.Message) ? null : a.Message,
|
||||||
|
CreatedAt = Instant.FromDateTimeOffset(a.CreatedAt.ToDateTimeOffset()),
|
||||||
|
UpdatedAt = Instant.FromDateTimeOffset(a.UpdatedAt.ToDateTimeOffset())
|
||||||
|
}));
|
||||||
|
post.Reactions.AddRange(proto.Reactions.Select(SnPostReaction.FromProtoValue));
|
||||||
|
post.Tags.AddRange(proto.Tags.Select(SnPostTag.FromProtoValue));
|
||||||
|
post.Categories.AddRange(proto.Categories.Select(SnPostCategory.FromProtoValue));
|
||||||
|
post.FeaturedRecords.AddRange(proto.FeaturedRecords.Select(SnPostFeaturedRecord.FromProtoValue));
|
||||||
|
|
||||||
|
if (proto.DeletedAt is not null)
|
||||||
|
post.DeletedAt = Instant.FromDateTimeOffset(proto.DeletedAt.ToDateTimeOffset());
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
public SnActivity ToActivity()
|
public SnActivity ToActivity()
|
||||||
{
|
{
|
||||||
return new SnActivity()
|
return new SnActivity()
|
||||||
@@ -314,8 +403,22 @@ public class SnPostFeaturedRecord : ModelBase
|
|||||||
{
|
{
|
||||||
proto.FeaturedAt = Timestamp.FromDateTimeOffset(FeaturedAt.Value.ToDateTimeOffset());
|
proto.FeaturedAt = Timestamp.FromDateTimeOffset(FeaturedAt.Value.ToDateTimeOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SnPostFeaturedRecord FromProtoValue(PostFeaturedRecord proto)
|
||||||
|
{
|
||||||
|
return new SnPostFeaturedRecord
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
PostId = Guid.Parse(proto.PostId),
|
||||||
|
SocialCredits = proto.SocialCredits,
|
||||||
|
CreatedAt = Instant.FromDateTimeOffset(proto.CreatedAt.ToDateTimeOffset()),
|
||||||
|
UpdatedAt = Instant.FromDateTimeOffset(proto.UpdatedAt.ToDateTimeOffset()),
|
||||||
|
FeaturedAt = proto.FeaturedAt != null ? Instant.FromDateTimeOffset(proto.FeaturedAt.ToDateTimeOffset()) : null
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PostReactionAttitude
|
public enum PostReactionAttitude
|
||||||
@@ -352,6 +455,7 @@ public class SnPostReaction : ModelBase
|
|||||||
{
|
{
|
||||||
proto.Account = Account.ToProtoValue();
|
proto.Account = Account.ToProtoValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,6 +526,7 @@ public class PostEmbedView
|
|||||||
{
|
{
|
||||||
proto.AspectRatio = AspectRatio.Value;
|
proto.AspectRatio = AspectRatio.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class SnPublisher : ModelBase, IIdentifiedResource
|
|||||||
|
|
||||||
public string ResourceIdentifier => $"publisher:{Id}";
|
public string ResourceIdentifier => $"publisher:{Id}";
|
||||||
|
|
||||||
public static SnPublisher FromProto(Proto.Publisher proto)
|
public static SnPublisher FromProtoValue(Proto.Publisher proto)
|
||||||
{
|
{
|
||||||
var publisher = new SnPublisher
|
var publisher = new SnPublisher
|
||||||
{
|
{
|
||||||
@@ -85,7 +85,7 @@ public class SnPublisher : ModelBase, IIdentifiedResource
|
|||||||
return publisher;
|
return publisher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Proto.Publisher ToProto()
|
public Proto.Publisher ToProtoValue()
|
||||||
{
|
{
|
||||||
var p = new Proto.Publisher()
|
var p = new Proto.Publisher()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class PublisherServiceGrpc(PublisherService service, AppDatabase db)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p is null) throw new RpcException(new Status(StatusCode.NotFound, "Publisher not found"));
|
if (p is null) throw new RpcException(new Status(StatusCode.NotFound, "Publisher not found"));
|
||||||
return new GetPublisherResponse { Publisher = p.ToProto() };
|
return new GetPublisherResponse { Publisher = p.ToProtoValue() };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<ListPublishersResponse> GetPublisherBatch(
|
public override async Task<ListPublishersResponse> GetPublisherBatch(
|
||||||
@@ -43,7 +43,7 @@ public class PublisherServiceGrpc(PublisherService service, AppDatabase db)
|
|||||||
if (ids.Count == 0) return new ListPublishersResponse();
|
if (ids.Count == 0) return new ListPublishersResponse();
|
||||||
var list = await db.Publishers.Where(p => ids.Contains(p.Id)).ToListAsync();
|
var list = await db.Publishers.Where(p => ids.Contains(p.Id)).ToListAsync();
|
||||||
var resp = new ListPublishersResponse();
|
var resp = new ListPublishersResponse();
|
||||||
resp.Publishers.AddRange(list.Select(p => p.ToProto()));
|
resp.Publishers.AddRange(list.Select(p => p.ToProtoValue()));
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ public class PublisherServiceGrpc(PublisherService service, AppDatabase db)
|
|||||||
|
|
||||||
var list = await query.ToListAsync();
|
var list = await query.ToListAsync();
|
||||||
var resp = new ListPublishersResponse();
|
var resp = new ListPublishersResponse();
|
||||||
resp.Publishers.AddRange(list.Select(p => p.ToProto()));
|
resp.Publishers.AddRange(list.Select(p => p.ToProtoValue()));
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user