D.Mentia
D.Mentia
CC#
Created by D.Mentia on 7/6/2023 in #help
❔ Handling OpenAI/GPT API Functions with strong types
This is a fun design problem and really I have no idea how to approach it, any thoughts are welcome. I have one solution that I've hacked my way towards, which I'll share, but want to make it feel less hacky For ChatGPT, you can define Functions in the json which it can choose to 'execute' instead of responding to a query. 'Executing' a function just means it responds with a message containing Role=Function, Name={FunctionName}, and Arguments={JsonArguments (which you defined a schema for)}. When a function is executed, GPT expects you to return a message with Role=Function, Name={FunctionName}, Content={FunctionOutput}, and let it respond again (and it might choose to do another function, or it might respond to the user and basically end the chain) The data it sends for a 'function call' matches the definition you gave it for the function, which looks like:
{
"name": "get_n_day_weather_forecast",
"description": "Get an N-day weather forecast",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"format": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to use. Infer this from the users location.",
},
"num_days": {
"type": "integer",
"description": "The number of days to forecast",
}
},
"required": ["location", "format", "num_days"]
},
},
{
"name": "get_n_day_weather_forecast",
"description": "Get an N-day weather forecast",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"format": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to use. Infer this from the users location.",
},
"num_days": {
"type": "integer",
"description": "The number of days to forecast",
}
},
"required": ["location", "format", "num_days"]
},
},
I'd like to set up something to strongly type all of this. I want to define a C# method with strongly typed inputs and outputs, maybe register it in startup with DI, and have reflection or similar create everything needed to 'register' it. And also to provide some simple way to execute those functions when requested
36 replies
CC#
Created by D.Mentia on 1/30/2023 in #help
❔ Events vs Tasks
In what situation would you use an Event over a Task that might wrap the event? As an example:
private Component dragItem;

public void OnMousePressed(EventArgs e)
{
dragItem = getItem(e.Position);
}

public void OnMouseReleased(EventArgs e)
{
dragItem.Position = e.Position;
}
private Component dragItem;

public void OnMousePressed(EventArgs e)
{
dragItem = getItem(e.Position);
}

public void OnMouseReleased(EventArgs e)
{
dragItem.Position = e.Position;
}
vs
public async void OnMousePressed(EventArgs e)
{
var dragItem = getItem(e.Position);
var released = await MouseReleasedEvent();
dragItem.Position = released.Position;
}
public async void OnMousePressed(EventArgs e)
{
var dragItem = getItem(e.Position);
var released = await MouseReleasedEvent();
dragItem.Position = released.Position;
}
The second implementation is (obviously, to me) cleaner and easier to understand, all the logic together and with its vars staying in scope, for the situations where a process relies on multiple events.
Are there any memory or performance concerns with doing it that way? Or other good reasons to not do that (Assuming I am just wrapping a basic Event with a TaskCompletionSource that's handled somewhere else) (And assuming I'm using async Events correctly, which I haven't really looked into how to do them without async voiding but I assume it's possible...)
13 replies
CC#
Created by D.Mentia on 1/20/2023 in #help
❔ DTOs Pros and Cons
Backstory: At work, we use models that contain DB entities, and we expect them to be partial; they should be Selected from the DB with only the minimal required data. I'm trying to convince them to use real DTOs instead, and what I'm hoping to do here is list my arguments, and see if anyone has any arguments against them that I should consider To be clear, instead of
class PatchMyEntityModel
{
MyEntity Entity {get; set;}
}
class PatchMyEntityModel
{
MyEntity Entity {get; set;}
}
I want
class PatchMyEntityModel
{
required Guid EntityGlobalId {get; init;}
string? Name {get; init;}
DateTime? Date {get; init;}
// ... etc
}
class PatchMyEntityModel
{
required Guid EntityGlobalId {get; init;}
string? Name {get; init;}
DateTime? Date {get; init;}
// ... etc
}
(Especially because the endpoint only supports updating a few specific properties, not all of them; entities might still be used in DTOs when they are actually needed, but ideally only internally) If I'm confused about what a 'DTO' is, let me know, though. I just want them to have explicit properties so I don't have to hunt through a chain of 12 methods that will be called with my model, to see what they all expect me to pass And yeah I'd rather use records, but there are some reasons we can't do that just yet DTO Pros: 1. Explicit properties that clearly show what data is required for this endpoint 2. Explicit return values - consumers don't have to wonder if a null might just mean we didn't Select that column 3. The capabilities of the endpoint are clearer 4. The caller can't include more data than necessary; reduces network overhead 5. Compile time errors instead of runtime errors, if giving the wrong arguments to an endpoint or trying to use return values that are not supplied from it - Runtime errors are a HUGE problem, the main thing I'm trying to solve here, and they are common when using entities because callers may not include the correct data in the entity, or may expect data in the returned entity that isn't actually populated 6. Decouples MyEntity from the API, MyEntity can be freely modified without affecting clients or endpoints using DTOs (as long as it can still fit existing DTOs) 7. Allows specific intellisense-capable documentation comments on each property DTO Cons: 1. Maintenance; if a DB entity is changed, in some cases, all DTOs using the changed column must be updated alongside the logic. This may (at most) double the number of places in the code you need to modify for an entity change (But even with entities, the logic still has to be updated, and all consumers must update their logic and your package - even if the change was superficial like a rename, which DTOs could allow without making the clients aware of it. Plus, updating the DTO is no different from updating a method signature, or updating an interface; it's not hard, the compiler tells you where and what to do, and it's worth it to have a contract) And... that's it, I think. Thoughts?
Clearly I seem to be underestimating the cost of maintenance, https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/august/pros-and-cons-of-data-transfer-objects seems to imply that it's significant, but I just can't imagine a scenario where maintenance for DTOs is significantly more work than without.
Any time a DTO would have to be modified, the logic using an entity would also have to be modified. It's not at all hard, while you're there, to F12 to the DTO and update that too - that may even be the first thing you do, it's no different from updating a method signature if you're changing what that method takes. DTOs also allow some of those changes to the entity without changing the DTOs or forcing clients to update, whereas passing around entities does not allow that in any case
9 replies
CC#
Created by D.Mentia on 12/4/2022 in #help
❔ Designing a Worker system for a game
I've had a game idea for a long time and figured you guys would give good critique on one of the pieces of the design that I've been struggling with - managing Jobs, Workers, and Resources (together) The idea is simple enough; Surviving Mars, Rimworld, or any colony management game. I want Workers, who are automatically assigned Jobs as needed based on proximity to the job, and possibly other conditions Summarizing it here will be less simple but I'll try to make it very brief...
Recipe - Each class inheriting this contains a hardcoded set of Tasks (not C# Tasks) that must be completed for the recipe to produce an output, and info about inputs/outputs
Task - Contains the required Resource type, and the location it must be delivered to (and/or info for other job types, Worker requirements, etc). May contain an inner set of Tasks which must be completed before the primary Task can be completed. Contains methods to mark as completed, which are called by the Worker they are assigned to, and emits events when completed
JobGiver - Contains Recipes. Emits JobReady events containing Tasks for a recipe. Subscribes to events for completion of each primary Task, and produces the Recipe output when all Tasks for a Recipe are complete
Worker - Has a single ActiveTask. Emits WorkerReady event when it has no Task
ActiveTask - Inherits Task, includes a direct reference to the specific Resource instance that is reserved by this Task JobManager - Subscribes to all WorkerReady events, tracks available workers and their positions. Subscribes to JobReady events; queues Tasks if workers or resources are unavailable. Creates and assigns ActiveTasks when a valid resource and worker are found for the Task. Tracks reserved and unreserved resource instances by listening to appropriate world events This is the barest essence of my idea so far. But I have some concerns... - Tracking every resource instance could get very expensive, not to mention checking distance between every worker, the JobGiver, and every potential resource (Realistically I'd probably pick one to prioritize; minimum distance from resource to JobGiver, or minimum from worker to resource) - Should the JobGiver be responsible for checking its own inventory contents, and marking tasks complete if the resources are already available? Or should the JobManager handle that? - Relying on events means that I'll have to make special cases for anytime a player can mess with something; taking a reserved resource, for example. But that'd just be re-queuing the Task It's also worth pointing out that the reason for tracking resources is because we need to reserve them; we don't want two Workers trying to grab the same resource, and we don't want Workers taking resources that are stored and about to be used for some Recipe Anyway, I'd love any ideas or critique on the high-level design
5 replies
CC#
Created by D.Mentia on 12/1/2022 in #help
❔ How to structure DTOs
If you're making a DTO, do you make it like this
public class UpdateStudentDTO
{
public int Id;
public string? Name;
public string? Address; // etc
}
public class UpdateStudentDTO
{
public int Id;
public string? Name;
public string? Address; // etc
}
Or like this
public class StudentDTO{
OperationTypes Type; // OperationTypes.Update, .Create, .Delete
public int? Id;
public string? Name; // etc
}
public class StudentDTO{
OperationTypes Type; // OperationTypes.Update, .Create, .Delete
public int? Id;
public string? Name; // etc
}
Or like this
public class UpdateStudentDTO{
public int Id;
public Student FieldsToUpdate;
}
public class UpdateStudentDTO{
public int Id;
public Student FieldsToUpdate;
}
Basically, should there be one DTO that is used for all OperationTypes, or one endpoint and DTO per type (Update/Delete/Etc)? And the last one is quite bad, I'm sure, but if the Student is complex and long, would it ever be acceptable?
26 replies
CC#
Created by D.Mentia on 11/28/2022 in #help
❔ Game Design Brainstorm - Workers, Jobs
Not Unity's Workers or Jobs; I have an idea for a colony simulator style game, but keep struggling coming up with a good design for handling the jobs system and specifically, resource reservation.
I figured you guys could come up with lots of interesting ideas, if you find the problem interesting. I'm looking for something similar to Surviving Mars; very automated, intended to encourage more workers than jobs (to handle bursts), with little player interaction, and the main challenge is the logistics of keeping things close to reduce travel times. Not like Rimworld, with lots of fiddling with priority and more jobs than workers Desired behavior: Basically, when a Job is possible, the nearest available Worker is assigned to do it. As a simple example, we'll say we have a Steel Factory, which requires 2 iron ingots, and 100 units of 'work', to produce 1 Steel Sheet. For simplicity we'll assume one recipe per machine, and each Worker can carry one item in its inventory When that factory is turned on, 2 workers should each retrieve an iron ingot from the nearest location, and bring it to the factory. This is where resource reservation comes in; we don't want multiple workers trying to grab the same resource and having to reroute. We also don't even want to tie up workers when all the iron ingots are already reserved for other jobs When the factory has the resources it needs for a recipe, a worker should be assigned to 'work' the factory to create the recipe output. A worker then delivers that output to a storage facility, and the process repeats Extra concepts: - Workers may have a Class and level, and certain jobs may require a specific class and level of worker - Jobs include things like, delivering to and building blueprints, delivering resources to storage, delivering resources to a machine, working a machine; the design should be generic enough to apply to all of them I have a big ole design ready, but it feels clunky and every time I try to use it I feel like something's wrong. I'm not sure if I want a Resource<T> or an IronIngot : IResource, for example.
Here's an early pass at that, though I'm still tweaking it: https://paste.mod.gg/pbwbwzttbrum/0 And again, I'm just asking for ideas on the patterns and structures to use for this. Performance will likely be a concern, I'm assuming thousands of workers and jobs at any given point (but DOTS can power through a lot of overhead).
2 replies
CC#
Created by D.Mentia on 11/12/2022 in #help
Turning an Event into a Task [Answered]
I have a method that needs to send a message over the network, and then wait for a response before returning something. Due to some quirks, the only way to get a response is via an event. What is an appropriate way to have the method wait for the event, and get the data from it?
I have tried setting up a TaskCompletionSource in a private field, that the method awaits, and the event sets the result - but the event could trigger from code I didn't write, and basically could end up returning the wrong data. And having to use a private field feels bad, though I don't know what alternatives I have
68 replies
CC#
Created by D.Mentia on 11/11/2022 in #help
❔ Task.WhenAll for Tests, and other async questions
In xUnit, there is no support for testing async or in parallel within the same collection, where a collection is basically tests that share a single startup and teardown So I extended some xUnit classes, found where the tests are ran, and instead of awaiting each one in a loop, I start them all and then Task.WhenAll. This makes the tests really fast, and when they pass, verifies there are no concurrency issues Why would this be a bad idea? I assume there must be a reason they did not do this by default And, would this be considered Parallel? I always thought async and Parallel are different, even opposite things, but if not awaiting them immediately, then it is parallel, isn't it? And lastly, should I do some sort of batching, or degrees of parallelism, when doing a large number of Task.WhenAll (in general)? Or is it just going to handle that performantly on its own?
32 replies