Compare commits
13 Commits
Author | SHA1 | Date |
---|---|---|
|
60ef1deb90 | |
|
1aaa1d0e07 | |
|
0bf0fe3b01 | |
|
aa2483e255 | |
|
aebfe325d6 | |
|
49ad81bc27 | |
|
559e276c44 | |
|
f0cea5973d | |
|
fe0c0d8f4f | |
|
f08ab50818 | |
|
abc5bee273 | |
|
76c088fea5 | |
|
5d62516c40 |
5
Makefile
5
Makefile
|
@ -1,4 +1,4 @@
|
||||||
all: clean chat.pb.go goimports vet
|
all: clean chat.pb.go book.pb.go goimports vet
|
||||||
|
|
||||||
goimports:
|
goimports:
|
||||||
goimports -w *.go
|
goimports -w *.go
|
||||||
|
@ -6,6 +6,9 @@ goimports:
|
||||||
chat.pb.go: chat.proto
|
chat.pb.go: chat.proto
|
||||||
autogenpb --proto chat.proto
|
autogenpb --proto chat.proto
|
||||||
|
|
||||||
|
book.pb.go: book.proto
|
||||||
|
autogenpb --proto book.proto
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.pb.go *.patch
|
rm -f *.pb.go *.patch
|
||||||
-rm -f go.*
|
-rm -f go.*
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package chatpb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// returns true if the pb was added
|
||||||
|
// to indicate that ConfigSave() should be run to write it out to disk
|
||||||
|
func (c *Chat) AddGeminiRequest(fname string, age time.Time, pb *GeminiRequest) bool {
|
||||||
|
for _, e := range c.GetEntries() {
|
||||||
|
if e.GetContentFile() == fname {
|
||||||
|
log.Info("fname already here", fname)
|
||||||
|
if iContent, iParts, ok := e.VerifyGeminiRequest(pb); ok {
|
||||||
|
log.Info("pb is already here with same size", iContent, iParts)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
log.Info("pb is already here but things don't match", iContent, iParts)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info("Adding new ChatEntry for", "/tmp/"+fname)
|
||||||
|
e := new(ChatEntry)
|
||||||
|
e.Ctime = timestamppb.New(age)
|
||||||
|
e.From = Who_USER
|
||||||
|
e.ContentFile = fname
|
||||||
|
e.GeminiRequest = pb
|
||||||
|
c.AppendEntry(e)
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package chatpb;
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "google/protobuf/struct.proto";
|
||||||
|
import "chat.proto";
|
||||||
|
|
||||||
|
message Book {
|
||||||
|
string uuid = 1; // `autogenpb:unique` `autogenpb:sort`
|
||||||
|
google.protobuf.Timestamp ctime = 2;
|
||||||
|
string Title = 3;
|
||||||
|
int32 version = 4;
|
||||||
|
Who from = 5;
|
||||||
|
string content = 6;
|
||||||
|
Table table = 7;
|
||||||
|
GeminiRequest GeminiRequest = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Books { // `autogenpb:marshal` `autogenpb:mutex` `autogenpb:gui`
|
||||||
|
string uuid = 1; // `autogenpb:uuid:8b6409ad-4498-43a6-b09a-7835c00dcb9a`
|
||||||
|
string version = 2; // `autogenpb:version:v0.0.1`
|
||||||
|
repeated Book Books = 3; // THIS MUST BE Chat and then Chats
|
||||||
|
google.protobuf.Timestamp ctime = 4;
|
||||||
|
string Title = 5;
|
||||||
|
string TitleUuid = 6;
|
||||||
|
}
|
232
chat.proto
232
chat.proto
|
@ -2,63 +2,217 @@ syntax = "proto3";
|
||||||
|
|
||||||
package chatpb;
|
package chatpb;
|
||||||
|
|
||||||
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "google/protobuf/struct.proto";
|
||||||
|
|
||||||
message Row { // `autogenpb:nomutex`
|
// Main request structure
|
||||||
repeated string fields = 1;
|
|
||||||
|
message GeminiRequest { // `autogenpb:marshal` `autogenpb:mutex`
|
||||||
|
string model = 1;
|
||||||
|
Config config = 2;
|
||||||
|
repeated Content contents = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Table { // `autogenpb:nomutex`
|
// Abort signal for the request
|
||||||
int32 columns = 1;
|
message AbortSignal {
|
||||||
repeated Row rows = 2;
|
}
|
||||||
|
|
||||||
|
message Schema {
|
||||||
|
string type = 1;
|
||||||
|
map<string, Schema> properties = 2;
|
||||||
|
repeated string required = 3;
|
||||||
|
string description = 4;
|
||||||
|
Schema items = 5;
|
||||||
|
repeated string enum = 6;
|
||||||
|
int32 minimum = 7;
|
||||||
|
int32 minLength = 8;
|
||||||
|
int32 minItems = 9;
|
||||||
|
google.protobuf.Value default_value = 10 [json_name = "default"];
|
||||||
|
}
|
||||||
|
|
||||||
|
message FunctionDeclaration {
|
||||||
|
string name = 1;
|
||||||
|
string description = 2;
|
||||||
|
Schema parameters_json_schema = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GoogleSearch {
|
||||||
|
}
|
||||||
|
|
||||||
|
message Tool {
|
||||||
|
repeated FunctionDeclaration functionDeclarations = 1;
|
||||||
|
GoogleSearch googleSearch = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuration for the request
|
||||||
|
message Config {
|
||||||
|
message ThinkingConfig {
|
||||||
|
bool includeThoughts = 1;
|
||||||
|
int32 thinkingBudget = 2;
|
||||||
|
}
|
||||||
|
double temperature = 2;
|
||||||
|
double topP = 3;
|
||||||
|
string systemInstruction = 4;
|
||||||
|
ResponseJsonSchema responseJsonSchema = 5;
|
||||||
|
string responseMimeType = 6;
|
||||||
|
AbortSignal abort_signal = 7;
|
||||||
|
ThinkingConfig thinkingConfig = 8;
|
||||||
|
repeated Tool tools = 9;
|
||||||
|
}
|
||||||
|
// JSON schema for the response
|
||||||
|
message ResponseJsonSchema {
|
||||||
|
string type = 1;
|
||||||
|
Properties properties = 2;
|
||||||
|
repeated string required = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Properties within the JSON schema
|
||||||
|
message Properties {
|
||||||
|
Reasoning reasoning = 1;
|
||||||
|
NextSpeaker next_speaker = 2;
|
||||||
|
CorrectedNewStringEscaping corrected_new_string_escaping = 3;
|
||||||
|
CorrectedTargetSnippet corrected_target_snippet = 4;
|
||||||
|
Confidence confidence = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Confidence {
|
||||||
|
string type = 1;
|
||||||
|
string description = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CorrectedTargetSnippet {
|
||||||
|
string type = 1;
|
||||||
|
string description = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reasoning property
|
||||||
|
message CorrectedNewStringEscaping {
|
||||||
|
string type = 1;
|
||||||
|
string description = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Reasoning {
|
||||||
|
string type = 1;
|
||||||
|
string description = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next speaker property
|
||||||
|
message NextSpeaker {
|
||||||
|
string type = 1;
|
||||||
|
repeated string enum = 2;
|
||||||
|
string description = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content of the chat
|
||||||
|
message Content {
|
||||||
|
string role = 1;
|
||||||
|
repeated Part parts = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part of the content
|
||||||
|
message Part {
|
||||||
|
oneof part_type {
|
||||||
|
string text = 1;
|
||||||
|
FunctionCall functionCall = 2;
|
||||||
|
FunctionResponse functionResponse = 3;
|
||||||
|
}
|
||||||
|
string thoughtSignature = 4;
|
||||||
|
}
|
||||||
|
// Function call
|
||||||
|
message FunctionCall {
|
||||||
|
string name = 1;
|
||||||
|
argsInfo args = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message argsInfo {
|
||||||
|
string absolute_path = 1;
|
||||||
|
string description = 2;
|
||||||
|
string command = 3;
|
||||||
|
string new_string = 4;
|
||||||
|
string old_string = 5;
|
||||||
|
int32 expected_replacements = 6;
|
||||||
|
string file_path = 7;
|
||||||
|
string directory = 8;
|
||||||
|
string path = 9;
|
||||||
|
string thinkingConfig = 10;
|
||||||
|
string pattern = 11;
|
||||||
|
string content = 12;
|
||||||
|
string fact = 13;
|
||||||
|
repeated string paths = 14;
|
||||||
|
string query = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function response
|
||||||
|
message FunctionResponse {
|
||||||
|
string id = 1;
|
||||||
|
string name = 2;
|
||||||
|
Response response = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response from a function call
|
||||||
|
message Response {
|
||||||
|
string output = 1;
|
||||||
|
string error = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Row {
|
||||||
|
repeated string fields = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Table {
|
||||||
|
int32 columns = 1;
|
||||||
|
repeated Row rows = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Who {
|
enum Who {
|
||||||
NOONE = 0;
|
NOONE = 0;
|
||||||
REGEX = 1;
|
REGEX = 1;
|
||||||
USER = 2;
|
USER = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: A message to hold all the details of a tool call
|
message ToolCall {
|
||||||
message ToolCall { // `autogenpb:nomutex`
|
string name = 1;
|
||||||
string name = 1; // e.g., "Shell"
|
string input = 2;
|
||||||
string input = 2; // The command that was run
|
string description = 3;
|
||||||
string description = 3; // The description for the command
|
string output_stdout = 4;
|
||||||
string output_stdout = 4;
|
string output_stderr = 5;
|
||||||
string output_stderr = 5;
|
int32 exit_code = 6;
|
||||||
int32 exit_code = 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message CodeSnippet { // `autogenpb:nomutex`
|
message CodeSnippet {
|
||||||
string filename = 1;
|
string filename = 1;
|
||||||
string content = 2;
|
string content = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ChatEntry { // `autogenpb:nomutex`
|
message ChatEntry { // `autogenpb:marshal`
|
||||||
Who from = 1;
|
Who from = 1;
|
||||||
google.protobuf.Timestamp ctime = 2;
|
google.protobuf.Timestamp ctime = 2;
|
||||||
string content = 3;
|
string content = 3;
|
||||||
Table table = 4;
|
Table table = 4;
|
||||||
repeated ToolCall ToolCalls = 5;
|
repeated ToolCall ToolCalls = 5;
|
||||||
string ContentFile = 6;
|
string ContentFile = 6;
|
||||||
string uuid = 7; // `autogenpb:unique` `autogenpb:sort`
|
string uuid = 7;
|
||||||
repeated CodeSnippet Snippets = 8;
|
repeated CodeSnippet Snippets = 8;
|
||||||
|
repeated Part parts = 9;
|
||||||
|
GeminiRequest GeminiRequest = 10;
|
||||||
|
int32 RequestCounter = 11;
|
||||||
|
repeated string paths = 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SessionStats { // `autogenpb:nomutex`
|
message SessionStats {
|
||||||
string uuid = 1;
|
string uuid = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Chat { // `autogenpb:nomutex`
|
message Chat {
|
||||||
string uuid = 1; // `autogenpb:unique` `autogenpb:sort`
|
string uuid = 1; // `autogenpb:unique` `autogenpb:sort`
|
||||||
google.protobuf.Timestamp ctime = 2;
|
google.protobuf.Timestamp ctime = 2;
|
||||||
string ChatName = 3;
|
string ChatName = 3;
|
||||||
repeated ChatEntry Entries = 4;
|
repeated ChatEntry Entries = 4;
|
||||||
repeated SessionStats Session = 5;
|
repeated SessionStats Session = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Chats { // `autogenpb:marshal` `autogenpb:mutex`
|
message Chats { // `autogenpb:marshal` `autogenpb:mutex` `autogenpb:gui`
|
||||||
string uuid = 1; // `autogenpb:uuid:9fd31f10-c25d-4d66-bc8d-5f6eb7c79057`
|
string uuid = 1; // `autogenpb:uuid:9fd31f10-c25d-4d66-bc8d-5f6eb7c79057` `autogenpb:primary`
|
||||||
string version = 2; // `autogenpb:version:v0.0.1`
|
string version = 2; // `autogenpb:version:v0.0.1`
|
||||||
repeated Chat Chats = 3; // THIS MUST BE Chat and then Chats
|
repeated Chat Chats = 3; // THIS MUST BE Chat and then Chats
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package chatpb
|
||||||
|
|
||||||
|
// functions to import and export the protobuf
|
||||||
|
// data to and from config files
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"go.wit.com/lib/protobuf/bugpb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// write to ~/.config/regex/ unless ENV{REGEX_HOME} is set
|
||||||
|
func (all *Books) ConfigSave() error {
|
||||||
|
if os.Getenv("REGEX_HOME") == "" {
|
||||||
|
homeDir, _ := os.UserHomeDir()
|
||||||
|
fullpath := filepath.Join(homeDir, ".config/regex")
|
||||||
|
os.Setenv("REGEX_HOME", fullpath)
|
||||||
|
}
|
||||||
|
if all == nil {
|
||||||
|
log.Warn("chatpb all == nil")
|
||||||
|
return errors.New("chatpb.Books.ConfigSave() all == nil")
|
||||||
|
}
|
||||||
|
log.Info("Books.ConfigSave()")
|
||||||
|
|
||||||
|
// --- Start of Fix ---
|
||||||
|
// Create a new, clean Books object to avoid marshaling a slice with nil entries.
|
||||||
|
cleanBooks := NewBooks() // Assuming NewBooks() initializes the struct correctly.
|
||||||
|
|
||||||
|
// Loop through the original books and append only the non-nil ones.
|
||||||
|
for _, book := range all.GetBooks() {
|
||||||
|
if book != nil {
|
||||||
|
cleanBooks.Books = append(cleanBooks.Books, book)
|
||||||
|
} else {
|
||||||
|
log.Warn("Found and skipped a nil book entry during Books.ConfigSave")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- End of Fix ---
|
||||||
|
|
||||||
|
data, err := cleanBooks.Marshal() // Marshal the clean object, not 'all'
|
||||||
|
if err != nil {
|
||||||
|
log.Info("chatpb proto.Marshal() failed len", len(data), err)
|
||||||
|
// The tryValidate logic might be less necessary now but kept for safety.
|
||||||
|
if err := cleanBooks.tryValidate(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
data, err = cleanBooks.Marshal() // Retry with the clean object
|
||||||
|
if err == nil {
|
||||||
|
log.Info("chatpb.Books.ConfigSave() pb.Marshal() worked after validation len", len(cleanBooks.Books), "books")
|
||||||
|
configWrite("regex.pb", data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := "book." + all.GetTitleUuid() + ".pb"
|
||||||
|
if err := configWrite(filename, data); err != nil {
|
||||||
|
log.Infof("chatpb.Books.ConfigSave() failed len(Books)=%d bytes=%d", len(cleanBooks.Books), len(data))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (all *Books) tryValidate() error {
|
||||||
|
err := bugpb.ValidateProtoUTF8(all)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Protobuf UTF-8 validation failed: %v\n", err)
|
||||||
|
}
|
||||||
|
if err := bugpb.SanitizeProtoUTF8(all); err != nil {
|
||||||
|
log.Warn("Sanitation failed:", err)
|
||||||
|
// log.Fatalf("Sanitization failed: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
33
config.go
33
config.go
|
@ -10,6 +10,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/lib/gui/shell"
|
||||||
"go.wit.com/lib/protobuf/bugpb"
|
"go.wit.com/lib/protobuf/bugpb"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
@ -25,6 +26,7 @@ func (all *Chats) ConfigSave() error {
|
||||||
log.Warn("chatpb all == nil")
|
log.Warn("chatpb all == nil")
|
||||||
return errors.New("chatpb.ConfigSave() all == nil")
|
return errors.New("chatpb.ConfigSave() all == nil")
|
||||||
}
|
}
|
||||||
|
log.Info("DOING ConfigSave()")
|
||||||
|
|
||||||
// --- Start of Fix ---
|
// --- Start of Fix ---
|
||||||
// Create a new, clean Chats object to avoid marshaling a slice with nil entries.
|
// Create a new, clean Chats object to avoid marshaling a slice with nil entries.
|
||||||
|
@ -46,7 +48,7 @@ func (all *Chats) ConfigSave() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("chatpb proto.Marshal() failed len", len(data), err)
|
log.Info("chatpb proto.Marshal() failed len", len(data), err)
|
||||||
// The tryValidate logic might be less necessary now but kept for safety.
|
// The tryValidate logic might be less necessary now but kept for safety.
|
||||||
if err := all.tryValidate(); err != nil {
|
if err := cleanChats.tryValidate(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
data, err = cleanChats.Marshal() // Retry with the clean object
|
data, err = cleanChats.Marshal() // Retry with the clean object
|
||||||
|
@ -61,6 +63,19 @@ func (all *Chats) ConfigSave() error {
|
||||||
|
|
||||||
// --- Backup Logic ---
|
// --- Backup Logic ---
|
||||||
filename := filepath.Join(os.Getenv("REGEX_HOME"), "regex.pb")
|
filename := filepath.Join(os.Getenv("REGEX_HOME"), "regex.pb")
|
||||||
|
filebackup := filepath.Join(os.Getenv("REGEX_HOME"), "regex.backup.pb")
|
||||||
|
if s, err := os.Stat(filebackup); err == nil {
|
||||||
|
log.Info("STAT OF CONFIG BACKUP WORKED", filebackup)
|
||||||
|
age := time.Since(s.ModTime())
|
||||||
|
if age > 10*time.Second {
|
||||||
|
log.Info(filebackup, "is greater than 10 minutes")
|
||||||
|
shell.RunVerbose([]string{"cp", filename, filebackup})
|
||||||
|
} else {
|
||||||
|
log.Info(filebackup, "is less than 10 minutes")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Info("STAT OF CONFIG BACKUP FAILED", filebackup)
|
||||||
|
}
|
||||||
if _, err := os.Stat(filename); err == nil {
|
if _, err := os.Stat(filename); err == nil {
|
||||||
// File exists, so back it up.
|
// File exists, so back it up.
|
||||||
dir := filepath.Dir(filename)
|
dir := filepath.Dir(filename)
|
||||||
|
@ -77,13 +92,12 @@ func (all *Chats) ConfigSave() error {
|
||||||
log.Infof("chatpb.ConfigSave() failed len(Chats)=%d bytes=%d", len(cleanChats.Chats), len(data))
|
log.Infof("chatpb.ConfigSave() failed len(Chats)=%d bytes=%d", len(cleanChats.Chats), len(data))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
configWrite("regex.text", []byte(cleanChats.FormatTEXT()))
|
// configWrite("regex.text", []byte(cleanChats.FormatTEXT()))
|
||||||
log.Infof("chatpb.ConfigSave() worked len(Chats)=%d bytes=%d", len(cleanChats.Chats), len(data))
|
// log.Infof("chatpb.ConfigSave() worked len(Chats)=%d bytes=%d", len(cleanChats.Chats), len(data))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (all *Chats) tryValidate() error {
|
func (all *Chats) tryValidate() error {
|
||||||
|
|
||||||
err := bugpb.ValidateProtoUTF8(all)
|
err := bugpb.ValidateProtoUTF8(all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Protobuf UTF-8 validation failed: %v\n", err)
|
log.Printf("Protobuf UTF-8 validation failed: %v\n", err)
|
||||||
|
@ -131,7 +145,7 @@ func (all *Chats) ConfigLoad() error {
|
||||||
log.Warn("versions do not match", test.Version, all.Version)
|
log.Warn("versions do not match", test.Version, all.Version)
|
||||||
deleteProtobufFile(cfgname)
|
deleteProtobufFile(cfgname)
|
||||||
}
|
}
|
||||||
log.Info(cfgname, "protobuf versions and uuid match", all.Uuid, all.Version)
|
// log.Info(cfgname, "protobuf versions and uuid match", all.Uuid, all.Version)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,15 +183,6 @@ func configWrite(filename string, data []byte) error {
|
||||||
log.Warn("open config file :", err)
|
log.Warn("open config file :", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if filename == "regex.text" {
|
|
||||||
// add header
|
|
||||||
cfgfile.Write([]byte("# this file is automatically re-generated from regex.pb, however,\n"))
|
|
||||||
cfgfile.Write([]byte("# if you want to edit it by hand, you can:\n"))
|
|
||||||
cfgfile.Write([]byte("# stop regex; remove regex.pb; edit regex.text; start regex\n"))
|
|
||||||
cfgfile.Write([]byte("# this will cause the default behavior to fallback to parsing this file for the config\n"))
|
|
||||||
cfgfile.Write([]byte("\n"))
|
|
||||||
cfgfile.Write([]byte("# this file is intended to be used to customize settings on what\n"))
|
|
||||||
}
|
|
||||||
cfgfile.Write(data)
|
cfgfile.Write(data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package chatpb
|
||||||
|
|
||||||
|
func (all *Chats) FindUuid(id string) *Chat {
|
||||||
|
for chat := range all.IterAll() {
|
||||||
|
if chat.Uuid == id {
|
||||||
|
return chat
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range chat.GetSession() {
|
||||||
|
if id == e.Uuid {
|
||||||
|
return chat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range chat.GetEntries() {
|
||||||
|
if id == e.Uuid {
|
||||||
|
return chat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
40
helpers.go
40
helpers.go
|
@ -93,3 +93,43 @@ func (x *Chats) AppendNew(y *Chat) {
|
||||||
|
|
||||||
x.Chats = append(x.Chats, chat)
|
x.Chats = append(x.Chats, chat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a Append() shortcut (that does Clone() with a mutex) notsure if it really works
|
||||||
|
func (x *Chat) AppendEntry(y *ChatEntry) {
|
||||||
|
x.Lock()
|
||||||
|
defer x.Unlock()
|
||||||
|
|
||||||
|
x.Entries = append(x.Entries, proto.Clone(y).(*ChatEntry))
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if the pb is probably the same
|
||||||
|
func (c *ChatEntry) VerifyGeminiRequest(pb *GeminiRequest) (int, int, bool) {
|
||||||
|
if c.GeminiRequest == nil {
|
||||||
|
log.Warn("There is no GeminiRequest in the chat protobuf")
|
||||||
|
return -1, -1, false
|
||||||
|
}
|
||||||
|
if pb == nil {
|
||||||
|
log.Warn("There is no GeminiRequest in the passed in protobuf")
|
||||||
|
return -1, -1, false
|
||||||
|
}
|
||||||
|
if len(c.GeminiRequest.GetContents()) != len(pb.GetContents()) {
|
||||||
|
log.Warn("c != pb", len(c.GeminiRequest.GetContents()), len(pb.GetContents()))
|
||||||
|
return -1, -1, false
|
||||||
|
}
|
||||||
|
var cCount int // # of Parts in all the Contents
|
||||||
|
var pbCount int // # of Parts in all the Contents
|
||||||
|
for _, grc := range c.GeminiRequest.GetContents() {
|
||||||
|
cCount += len(grc.GetParts())
|
||||||
|
|
||||||
|
}
|
||||||
|
for _, grc := range pb.GetContents() {
|
||||||
|
pbCount += len(grc.GetParts())
|
||||||
|
|
||||||
|
}
|
||||||
|
if cCount != pbCount {
|
||||||
|
log.Warn("c != pb", cCount, pbCount)
|
||||||
|
return len(c.GeminiRequest.GetContents()), cCount, false
|
||||||
|
}
|
||||||
|
log.Info("EVERYTHING MATCHED", cCount, pbCount, len(c.GeminiRequest.GetContents()), len(pb.GetContents()))
|
||||||
|
return len(c.GeminiRequest.GetContents()), cCount, true
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
// Copyright 2025 WIT.COM Inc Licensed GPL 3.0
|
||||||
|
|
||||||
|
package chatpb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.wit.com/lib/cobol"
|
||||||
|
"go.wit.com/lib/gui/shell"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (all *Chats) PrintHumanTable() {
|
||||||
|
log.DaemonMode(true)
|
||||||
|
|
||||||
|
// print the header
|
||||||
|
args := []string{"uuid", "name", "age", "master", "devel", "user", "curver", "lasttag", "next", "repo type"}
|
||||||
|
sizes := []int{40, 40, 6, 4, 4, 4, 4, 4, 4, 4}
|
||||||
|
log.Info(cobol.StandardTableSize10(sizes, args))
|
||||||
|
|
||||||
|
for chat := range all.IterAll() {
|
||||||
|
chat.printChatToTable(sizes)
|
||||||
|
}
|
||||||
|
log.Infof("Total Chats: %d\n", all.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chat) printChatToTable(sizes []int) {
|
||||||
|
var args []string
|
||||||
|
age := c.Ctime.AsTime().String()
|
||||||
|
args = []string{c.Uuid, age, c.GetChatName(), "", "", "", "", "", "", ""}
|
||||||
|
|
||||||
|
start := cobol.StandardTableSize10(sizes, args)
|
||||||
|
|
||||||
|
log.Info(start)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chat) PrintChatStatsTable() {
|
||||||
|
log.DaemonMode(true)
|
||||||
|
|
||||||
|
// print the header
|
||||||
|
args := []string{"uuid", "name", "age", "master", "devel", "user", "curver", "lasttag", "next", "repo type"}
|
||||||
|
sizes := []int{40, 40, 6, 4, 4, 4, 4, 4, 4, 4}
|
||||||
|
log.Info(cobol.StandardTableSize10(sizes, args))
|
||||||
|
|
||||||
|
for _, e := range c.GetSession() {
|
||||||
|
var args []string
|
||||||
|
args = []string{e.Uuid, "", "", "", "", "", "", "", "", ""}
|
||||||
|
|
||||||
|
start := cobol.StandardTableSize10(sizes, args)
|
||||||
|
|
||||||
|
log.Info(start)
|
||||||
|
}
|
||||||
|
log.Infof("Total Chats: %d\n", len(c.GetEntries()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chat) PrintChatEntriesTable() {
|
||||||
|
log.DaemonMode(true)
|
||||||
|
|
||||||
|
// print the header
|
||||||
|
args := []string{"uuid", "age", "con file", "Who", "model", "", "", "", "", ""}
|
||||||
|
sizes := []int{40, 16, 8, 4, 8, 2, 2, 2, 2, 2}
|
||||||
|
log.Info(cobol.StandardTableSize10(sizes, args))
|
||||||
|
|
||||||
|
for _, e := range c.GetEntries() {
|
||||||
|
var args []string
|
||||||
|
age := e.Ctime.AsTime().String()
|
||||||
|
args = []string{e.Uuid, age, e.GetContentFile(), e.From.String(), "", "", "", "", "", ""}
|
||||||
|
|
||||||
|
start := cobol.StandardTableSize10(sizes, args)
|
||||||
|
log.Info(start)
|
||||||
|
}
|
||||||
|
log.Infof("Total Chats: %d\n", len(c.GetEntries()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chat) PrintChatGeminiTable() {
|
||||||
|
log.DaemonMode(true)
|
||||||
|
|
||||||
|
// print the header
|
||||||
|
args := []string{"uuid", "age", "ID", "Who", "model", "", "", "", "", ""}
|
||||||
|
sizes := []int{40, 5, 5, 8, 16, 2, 2, 2, 2, 2}
|
||||||
|
log.Info(cobol.StandardTableSize10(sizes, args))
|
||||||
|
|
||||||
|
for _, e := range c.GetEntries() {
|
||||||
|
var args []string
|
||||||
|
dur := time.Since(e.Ctime.AsTime())
|
||||||
|
age := shell.FormatDuration(dur)
|
||||||
|
var model string
|
||||||
|
var id string
|
||||||
|
if e.GeminiRequest == nil {
|
||||||
|
model = "nil"
|
||||||
|
} else {
|
||||||
|
model = e.GeminiRequest.Model
|
||||||
|
}
|
||||||
|
if e.GetContentFile() != "" {
|
||||||
|
parts := strings.Split(e.GetContentFile(), ".")
|
||||||
|
if len(parts) < 4 {
|
||||||
|
id = "??"
|
||||||
|
} else {
|
||||||
|
id = parts[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args = []string{e.Uuid, age, id, e.From.String(), model, "", "", "", "", ""}
|
||||||
|
|
||||||
|
start := cobol.StandardTableSize10(sizes, args)
|
||||||
|
log.Info(start, e.GetContentFile())
|
||||||
|
}
|
||||||
|
log.Infof("Total Chats: %d\n", len(c.GetEntries()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *GeminiRequest) PrintGeminiTable() {
|
||||||
|
if gr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.DaemonMode(true)
|
||||||
|
|
||||||
|
// print the header
|
||||||
|
args := []string{"model", "what", "age", "cId", "partId", "", "", "", "", ""}
|
||||||
|
sizes := []int{16, 8, 4, 4, 4, 16, 120, 2, 2, 2}
|
||||||
|
log.Info(cobol.StandardTableSize10(sizes, args))
|
||||||
|
|
||||||
|
var countCONTENTS int
|
||||||
|
var countPARTS int
|
||||||
|
for x, c := range gr.GetContents() {
|
||||||
|
model := gr.Model
|
||||||
|
cId := log.Sprintf("%d", x)
|
||||||
|
countCONTENTS += 1
|
||||||
|
for i, p := range c.GetParts() {
|
||||||
|
var args []string
|
||||||
|
partId := log.Sprintf("%d", i)
|
||||||
|
|
||||||
|
// removes newline chars from the text
|
||||||
|
parts := strings.Split(p.GetText(), "\n")
|
||||||
|
txt := strings.Join(parts, "__")
|
||||||
|
|
||||||
|
what := "TXT"
|
||||||
|
|
||||||
|
if fc := p.GetFunctionCall(); fc != nil {
|
||||||
|
what = "FuncCall"
|
||||||
|
txt = log.Sprintf("%s, %v", fc.Name, fc.Args)
|
||||||
|
|
||||||
|
}
|
||||||
|
if fr := p.GetFunctionResponse(); fr != nil {
|
||||||
|
what = "FuncResp"
|
||||||
|
txt = fr.Name
|
||||||
|
txt = log.Sprintf("%s %s, %v", fr.Id, fr.Name, fr.Response)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = []string{model, what, "", cId, partId, p.ThoughtSignature, txt, "", "", ""}
|
||||||
|
|
||||||
|
start := cobol.StandardTableSize10(sizes, args)
|
||||||
|
log.Info(start)
|
||||||
|
countPARTS += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Infof("Total Contents (%d) Parts (%d)\n", countCONTENTS, countPARTS)
|
||||||
|
}
|
Loading…
Reference in New Issue