LocalAI
LocalAI copied to clipboard
refactor: Channel Refactor Split 1
In order to make #2062 less intimidating to review, I am going to create some smaller PRs to merge safe code over and get it out of the way!
This PR brings back over the following elements of #1963 ::
- pkg/utils/base64.go and a brand-new test file pkg/utils/base64_test.go - tests aren't fabulous but better than nothing.
- core/state.go - Not actually used yet, major sections commented out. Will be easier to bring over later splits if this is in place now though!
- Rename of BackendMonitorService and schema/whisper.go to schema/transcription.go and Result struct to TranscriptionResult
Deploy Preview for localai canceled.
Name | Link |
---|---|
Latest commit | 6dd72f79999b0eb8307c01c3248aa956f51fbe91 |
Latest deploy log | https://app.netlify.com/sites/localai/deploys/662fcc4bf0cfd200089ca840 |
cool! I think splitting brings benefits already, it's much easier to review the code :)
@dave-gray101 sorry to circle back late, what I mean is that you can have a channel of e.g. Jobs that executes something, and the Job results containing channels for the results. In the example below (which is a pattern that I end up using heavily) channels are used to signal that the work has been finished, but can be expanded easily : https://go.dev/play/p/6b0n5ISuYp-
package main
import (
"fmt"
"sync"
)
// Job represents a task that needs to be processed.
type Job struct {
ID int
JobData string
}
// JobResult holds the result of a job, along with a channel to signal completion.
type JobResult struct {
Result string
done chan struct{}
mu sync.Mutex
}
// NewJobResult initializes a new JobResult.
func NewJobResult() *JobResult {
return &JobResult{
done: make(chan struct{}),
}
}
// SetResult sets the result of a Job and signals that it's ready.
func (jr *JobResult) SetResult(result string) {
jr.mu.Lock()
defer jr.mu.Unlock()
jr.Result = result
close(jr.done) // Signal that the result is ready
}
// Wait blocks until the result is ready and then returns the result.
func (jr *JobResult) Wait() string {
<-jr.done // Wait for the result to be ready
jr.mu.Lock()
defer jr.mu.Unlock()
return jr.Result
}
func worker(jobs <-chan Job, results map[int]*JobResult) {
for job := range jobs {
go func(job Job) {
// Simulate some work
processResult := fmt.Sprintf("Processed Job: %d with data: %s", job.ID, job.JobData)
results[job.ID].SetResult(processResult)
}(job)
}
}
func submitJob(jobs chan<- Job, results map[int]*JobResult, id int, data string) *JobResult {
job := Job{ID: id, JobData: data}
result := NewJobResult()
results[job.ID] = result
jobs <- job
return result
}
func main() {
jobs := make(chan Job, 5)
results := make(map[int]*JobResult)
// Start the worker
go worker(jobs, results)
// Submit jobs and get results immediately
jobResults := []*JobResult{}
for i := 0; i < 3; i++ {
res := submitJob(jobs, results, i+1, fmt.Sprintf("data%d", i+1))
jobResults = append(jobResults, res)
}
close(jobs)
// Wait and print the results of each job
for _, res := range jobResults {
fmt.Println(res.Wait())
}
}
Some small nits from my side, but overall looks good - as we are refactoring and this changeset shouldn't introduce regressions, maybe makes sense to tackle some of the small nits now?
Looks good! nice cleanup!