Documentation ¶
Overview ¶
Package terminal provides an interface to interact with a Generative AI model in a terminal-based chat application. It manages the chat session, user input, AI communication, and displays the chat history.
The package is designed to be simple to use with a focus on a clean user experience. It includes functionality to handle graceful shutdowns, manage chat history, and simulate typing effects for AI responses.
Copyright (c) 2024 H0llyW00dzZ
Index ¶
- Constants
- func CheckLatestVersion(currentVersion string) (isLatest bool, latestVersion string, err error)
- func Colorize(text string, colorPairs []string, keepDelimiters map[string]bool) string
- func CountTokens(apiKey, input string) (int, error)
- func GetEmbedding(ctx context.Context, client *genai.Client, modelID, text string) ([]float32, error)
- func HandleCommand(input string, session *Session) (bool, error)
- func PrintPrefixWithTimeStamp(prefix string)
- func PrintTypingChat(message string, delay time.Duration)
- func RecoverFromPanic() func()deprecated
- func SendMessage(ctx context.Context, client *genai.Client, chatContext string, ...) (string, error)
- func SingleCharColorize(text string, delimiter string, color string) string
- type ANSIColorCodes
- type BinaryAnsiChars
- type ChatHistory
- type CommandHandler
- type DebugOrErrorLogger
- type GitHubRelease
- type NewLineChar
- type Session
- type TypingChars
Constants ¶
const ( // Note: By replacing the ANSI escape sequence from "\033" to "\x1b", might can avoid a rare bug that sometimes occurs on different machines, // although the original code works fine on mine (Author: @H0llyW00dzZ). ColorRed = "\x1b[31m" ColorGreen = "\x1b[32m" ColorYellow = "\x1b[33m" ColorBlue = "\x1b[34m" ColorPurple = "\x1b[35m" ColorCyan = "\x1b[36m" // ColorHex95b806 represents the color #95b806 using an ANSI escape sequence for 24-bit color. ColorHex95b806 = "\x1b[38;2;149;184;6m" // ColorCyan24Bit represents the color #11F0F7 using an ANSI escape sequence for 24-bit color. ColorCyan24Bit = "\x1b[38;2;17;240;247m" ColorPurple24Bit = "\x1b[38;2;255;0;255m" ColorReset = "\x1b[0m" )
ANSI color codes
const ( // GitHubAPIURL is the endpoint for the latest release information of the application. GitHubAPIURL = "https://api.github.com/repos/H0llyW00dzZ/GoGenAI-Terminal-Chat/releases/latest" GitHubReleaseFUll = "https://api.github.com/repos/H0llyW00dzZ/GoGenAI-Terminal-Chat/releases/tags/%s" // CurrentVersion represents the current version of the application. CurrentVersion = "v0.3.1" )
Defined List of GitHub API
const ( SignalMessage = "\nReceived an interrupt, shutting down gracefully..." RecoverGopher = "%sRecovered from panic:%s %s%v%s" ObjectHighLevelString = "%s %s" // Catch High level string ObjectTripleHighLevelString = "%%%s%%" // Catch High level triple string // TimeFormat is tailored for AI responses, providing a layout conducive to formatting chat transcripts. TimeFormat = "2006/01/02 15:04:05" StripChars = "---" NewLineChars = '\n' // this animated chars is magic, it used to show the user that the AI is typing just like human would type AnimatedChars = "%c" // this model is subject to changed in future ModelAi = "gemini-pro" // this may subject to changed in future for example can customize the delay TypingDelay = 60 * time.Millisecond )
Defined constants for the terminal package
const ( YouNerd = "🤓 You: " AiNerd = "🤖 AI: " ContextPrompt = "Hello! How can I assist you today?" ShutdownMessage = "Shutting down gracefully..." UnknownCommand = "Unknown command." ContextCancel = "Context canceled, shutting down..." // sending a messages to gopher officer ANewVersionIsAvailable = "A newer version is available: %s\n\n" ReleaseName = "- %s\n\n" FullChangeLog = "**%s**\n" // Better prompt instead of typing manually hahaha // // Note: These prompts are not persisted in the chat history retrieved by the ChatHistory.GetHistory() method. // Therefore, if you continue interacting with the AI after using these command prompts, // the conversation will resume from the point prior to the invocation of these commands. ApplicationName = "GoGenAI Terminal Chat" // Check Version Prompt commands YouAreusingLatest = "attempted a command: **%s**\n" + "The user is using Version **%s** of **%s**\n" + "This is the latest version.\n" + "Tell the user, No need to update." // Better Response for AI ReleaseNotesPrompt = "a user attempted a command: **%s**\n" + "The user is using Version **%s** of **%s**\n" + "A newer version is available: **%s**\n" + "Can you tell\n" + "Release Name: **%s**\n" + "%s" // Better Response for AI // Quit Prompt commands ContextPromptShutdown = "a user attempted an command: **%s** of **%s**\n" + "Please provide a shutdown message as you are AI." // Help Prompt commands HelpCommandPrompt = "**This a System messages**:**%s**\n\n" + "The user attempted an command: **%s**\n" + "Can you provide help information for the available commands?\n" + "List Command Available:\n**%s**\n**%s**" // Better Response for AI" )
Defined constants for language
const ( QuitCommand = ":quit" VersionCommand = ":checkversion" HelpCommand = ":help" PingCommand = ":ping" // Currently marked as TODO PrefixChar = ":" )
Defined constants for commands
Note: will add more in future based on the need, for example, to change the model, or to change the delay, another thing is syncing ai with goroutine (known as gopher)
const ( ErrorGettingShutdownMessage = "Error getting shutdown message from AI: %v" ErrorHandlingCommand = "Error handling command: %v" ErrorCountingTokens = "Error counting tokens: %v" ErrorSendingMessage = "Error sending message to AI: %v" ErrorReadingUserInput = "Error reading user input: %v" ErrorFailedToFetchReleaseInfo = "Failed to fetch the latest release info: %v" ErrorReceivedNon200StatusCode = "Received non-200 status code: %v" ErrorFailedToReadTheResponseBody = "Failed to read the response body: %v" ErrorFaileduUnmarshalTheReleaseData = "Failed to unmarshal the release data: %v" ErrorFailedTagToFetchReleaseInfo = "Failed to fetch release info for tag '%s': %v" ErrorFailedTagUnmarshalTheReleaseData = "Failed to unmarshal release data for tag '%s': %v" ErrorFailedTosendmessagesToAI = "Failed to send messages to AI: %v" ErrorFailedToCreateNewAiClient = "Failed to create new AI client: %v" ErrorFailedToStartAIChatSessionAttempt = "Failed to start AI chat session, attempt %d/%d" ErrorFailedtoStartAiChatSessionAfter = "Failed to start AI chat session after %d attempts" ErrorChatSessionisnill = "chat session is nil" ErrorFailedtoStartAiChatSession = "failed to start AI chat session" ErrorFailedToRenewSession = "Failed to renew session: %v" ErrorAiChatSessionStillNill = "AI chat session is still nil after renewal attempt" ErrorLowLevelFailedtoStartAiChatSession = "failed to start a new AI chat session: %w" ErrorUserAttemptUnrecognizedCommandPrompt = "**From System**:**%s**\n\nThe user attempted an unrecognized command: **%s**" // Better Response for AI ErrorFailedtoSendUnrecognizedCommandToAI = "Failed to send unrecognized command to AI: %v" HumanErrorWhileTypingCommandArgs = "Invalid Command Arguments" ErrorPingFailed = "Ping failed: %v" )
Defined List error message
const ( SingleAsterisk = "*" DoubleAsterisk = "**" SingleBacktick = "`" TripleBacktick = "```" StringNewLine = "\n" BinaryAnsiChar = '\x1b' BinaryAnsiSquenseChar = 'm' BinaryAnsiSquenseString = "m" )
Defined List of characters
const (
DEBUG_MODE = "DEBUG_MODE"
)
Defined List of Environment variables
const MaxChatHistory = 5 // Maximum number of messages to keep in history
Note: This is subject to change (for example, it can be customized in commands). For now, it's stable. Additionally, a token is inexpensive since, with Google AI's Gemini-Pro model, the maximum is 32K tokens.
Variables ¶
This section is empty.
Functions ¶
func CheckLatestVersion ¶ added in v0.2.1
CheckLatestVersion compares the current application version against the latest version available on GitHub. It fetches the latest release information from the repository specified by GitHubAPIURL and determines if an update is available.
Parameters:
currentVersion string: The version string of the currently running application.
Returns:
isLatest bool: A boolean indicating if the current version is the latest available. latestVersion string: The tag name of the latest release, if newer than current; otherwise, an empty string. err error: An error if the request fails or if there is an issue parsing the response.
func Colorize ¶ added in v0.1.6
Colorize applies ANSI color codes to the text surrounded by specified delimiters. It can process multiple delimiters, each with a corresponding color. The function can also conditionally retain or remove the delimiters in the final output.
Parameters:
text string: The text to be colorized. colorPairs []string: A slice where each pair of elements represents a delimiter and its color. keepDelimiters map[string]bool: A map to indicate whether to keep the delimiter in the output.
Returns:
string: The colorized text.
Note: This function may not work as expected in Windows Command Prompt due to its limited support for ANSI color codes. It is designed for terminals that support ANSI, such as those in Linux/Unix environments.
func CountTokens ¶ added in v0.1.2
CountTokens connects to a generative AI model using the provided API key and counts the number of tokens in the given input string. This function is useful for understanding the token usage of text inputs in the context of generative AI, which can help manage API usage and costs.
Parameters:
apiKey string: The API key used to authenticate with the generative AI service. input string: The text input for which the number of tokens will be counted.
Returns:
int: The number of tokens that the input string contains. error: An error that occurred while creating the client, connecting to the service, or counting the tokens. If the operation is successful, the error is nil.
The function creates a new client for each call, which is then closed before returning. It is designed to be a self-contained operation that does not require the caller to manage the lifecycle of the generative AI client.
Note: This function marked as TODO for now, since it is not used in the main because, a current version of chat system it's consider fully stable with better logic.
func GetEmbedding ¶ added in v0.1.2
func GetEmbedding(ctx context.Context, client *genai.Client, modelID, text string) ([]float32, error)
GetEmbedding computes the numerical embedding for a given piece of text using the specified generative AI model. Embeddings are useful for a variety of machine learning tasks, such as semantic search, where they can represent the meaning of text in a form that can be processed by algorithms.
Parameters:
ctx context.Context: The context for controlling the lifetime of the request. It allows the function to be canceled or to time out, and it carries request-scoped values. client *genai.Client: The client used to interact with the generative AI service. It should be already initialized and authenticated before calling this function. modelID string: The identifier for the embedding model to be used. This specifies which AI model will generate the embeddings. text string: The input text to be converted into an embedding.
Returns:
[]float32: An array of floating-point numbers representing the embedding of the input text. error: An error that may occur during the embedding process. If the operation is successful, the error is nil.
The function delegates the embedding task to the genai client's EmbeddingModel method and retrieves the embedding values from the response. It is the caller's responsibility to manage the lifecycle of the genai.Client, including its creation and closure.
Note: This function marked as TODO for now, since it is not used in the main because, a current version of chat system it's consider fully stable with better logic.
func HandleCommand ¶ added in v0.1.3
HandleCommand interprets the user input as a command and executes the associated action. It uses a map of command strings to their corresponding handler functions to manage different commands and their execution. If the command is recognized, the respective handler is called; otherwise, an unknown command message is displayed.
Parameters:
input string: The user input to be checked for commands. session *Session: The current chat session for context.
Returns:
bool: A boolean indicating if the input was a command and was handled. error: An error that may occur while handling the command.
func PrintPrefixWithTimeStamp ¶ added in v0.2.2
func PrintPrefixWithTimeStamp(prefix string)
PrintPrefixWithTimeStamp prints a message to the terminal, prefixed with a formatted timestamp. The timestamp is formatted according to the TimeFormat constant.
For example, with TimeFormat set to "2006/01/02 15:04:05" and the prefix "🤓 You: ", the output might be "2024/01/10 16:30:00 🤓 You:".
This function is designed for terminal outputs that benefit from a timestamped context, providing clarity and temporal reference for the message displayed.
The prefix parameter is appended to the timestamp and can be a log level, a descriptor, or any other string that aids in categorizing or highlighting the message.
func PrintTypingChat ¶
PrintTypingChat simulates the visual effect of typing out a message character by character. It prints each character of a message to the standard output with a delay between each character to give the appearance of real-time typing.
Parameters:
message string: The message to be displayed with the typing effect. delay time.Duration: The duration to wait between printing each character.
This function does not return any value. It directly prints to the standard output.
Note: This is particularly useful for simulating the Gopher's lifecycle (Known as Goroutines) events in a user-friendly manner. For instance, when a Gopher completes a task or job and transitions to a resting state, this function can print a message with a typing effect to visually represent the Gopher's "sleeping" activities.
func RecoverFromPanic
deprecated
added in
v0.2.2
func RecoverFromPanic() func()
RecoverFromPanic returns a deferred function that recovers from panics within a goroutine or function, preventing the panic from propagating and potentially causing the program to crash. Instead, it logs the panic information using the standard logger, allowing for post-mortem analysis without interrupting the program's execution flow.
Usage:
defer terminal.RecoverFromPanic()()
The function returned by RecoverFromPanic should be called by deferring it at the start of a goroutine or function. When a panic occurs, the deferred function will handle the panic by logging its message and stack trace, as provided by the recover built-in function.
Deprecated: This method is deprecated was replaced by logger.RecoverFromPanic.
func SendMessage ¶
func SendMessage(ctx context.Context, client *genai.Client, chatContext string, chatHistory ...string) (string, error)
SendMessage sends a chat message to the generative AI model and retrieves the response. It constructs a chat session using the provided `genai.Client`, which is used to communicate with the AI service. The function simulates a chat interaction by sending the chat context, optionally preceded by previous chat history, to the AI model.
Parameters:
ctx context.Context: The context for controlling the cancellation of the request. client *genai.Client: The client instance used to create a generative model session and send messages to the AI model. chatContext string: The chat context or message to be sent to the AI model. chatHistory ...string: An optional slice of strings representing previous chat history. If provided, it is prepended to the chatContext, separated by a newline, to provide context to the AI.
Returns:
string: The AI's response as a string, which includes the AI's message with a simulated typing effect. error: An error message if the message sending or response retrieval fails. If the operation is successful, the error is nil.
The function initializes a new chat session and sends the chat context, along with any provided chat history, to the generative AI model. It then calls `printResponse` to process and print the AI's response. The final AI response is returned as a concatenated string of all parts from the AI response.
func SingleCharColorize ¶ added in v0.1.8
SingleCharColorize applies ANSI color codes to text surrounded by single-character delimiters. It is particularly useful when dealing with text that contains list items or other elements that should be highlighted, and it ensures that the colorization is only applied to the specified delimiter at the beginning of a line.
Parameters:
text string: The text containing elements to be colorized. delimiter string: The single-character delimiter indicating the start of a colorizable element. color string: The ANSI color code to be applied to the elements starting with the delimiter.
Returns:
string: The resulting string with colorized elements as specified by the delimiter.
This function handles each line separately and checks for the presence of the delimiter at the beginning after trimming whitespace. If the delimiter is found, it colorizes the delimiter and the following character (typically a space). The rest of the line remains unaltered. If the delimiter is not at the beginning of a line, the line is added to the result without colorization.
Note: As with the Colorize function, SingleCharColorize may not function correctly in Windows Command Prompt or other environments that do not support ANSI color codes. It is best used in terminals that support these codes, such as most Linux/Unix terminals.
Types ¶
type ANSIColorCodes ¶ added in v0.2.9
type ANSIColorCodes struct { ColorRed string ColorGreen string ColorYellow string ColorBlue string ColorPurple string ColorCyan string ColorHex95b806 string // 24-bit color ColorCyan24Bit string // 24-bit color ColorPurple24Bit string // 24-bit color ColorReset string }
ANSIColorCodes defines a struct for holding ANSI color escape sequences.
type BinaryAnsiChars ¶ added in v0.3.0
type BinaryAnsiChars struct { BinaryAnsiChar rune BinaryAnsiSquenseChar rune BinaryAnsiSquenseString string }
BinaryAnsiChars is a struct that contains the ANSI characters used to print the typing effect.
type ChatHistory ¶
type ChatHistory struct {
Messages []string
}
ChatHistory holds the chat messages exchanged during a session. It provides methods to add new messages to the history and to retrieve the current state of the conversation.
func (*ChatHistory) AddMessage ¶
func (h *ChatHistory) AddMessage(user, text string)
AddMessage appends a new message to the chat history. It takes the username and the text of the message as inputs and formats them before adding to the Messages slice.
Parameters:
user string: The username of the individual sending the message. text string: The content of the message to be added to the history.
This method does not return any value or error. It assumes that all input is valid and safe to add to the chat history.
func (*ChatHistory) GetHistory ¶
func (h *ChatHistory) GetHistory() string
GetHistory concatenates all messages in the chat history into a single string, with each message separated by a newline character. This provides a simple way to view the entire chat history as a single text block.
Returns:
string: A newline-separated string of all messages in the chat history.
func (*ChatHistory) PrintHistory
deprecated
func (h *ChatHistory) PrintHistory()
PrintHistory outputs all messages in the chat history to the standard output, one message per line. This method is useful for displaying the chat history directly to the terminal.
Each message is printed in the order it was added, preserving the conversation flow. This method does not return any value or error.
Deprecated: This method is deprecated was replaced by GetHistory. It used to be used for debugging purposes while made the chat system without storage such as database.
func (*ChatHistory) RemoveMessages ¶ added in v0.2.5
func (h *ChatHistory) RemoveMessages(numMessages int, messageContent string)
RemoveMessages removes messages from the chat history. If a specific message is provided, it removes messages that contain that text; otherwise, it removes the specified number of most recent messages.
Parameters:
numMessages int: The number of most recent messages to remove. If set to 0 and a specific message is provided, all instances of that message are removed. messageContent string: The specific content of messages to remove. If empty, it removes the number of most recent messages specified by numMessages.
This method does not return any value. It updates the chat history in place.
Note: This currently marked as TODO since it's not used anywhere in the code. It's a good idea to add this feature in the future.
type CommandHandler ¶ added in v0.1.10
type CommandHandler interface { // Note: The list of command handlers here does not use os.Args; instead, it employs advanced idiomatic Go practices. 🤪 Execute(session *Session, parts []string) (bool, error) // new method IsValid(parts []string) bool // new method }
CommandHandler defines the function signature for handling chat commands. Each command handler function must conform to this signature.
type DebugOrErrorLogger ¶ added in v0.1.9
type DebugOrErrorLogger struct {
// contains filtered or unexported fields
}
DebugOrErrorLogger provides a simple logger with support for debug and error logging. It encapsulates a standard log.Logger and adds functionality for conditional debug logging and colorized error output.
func NewDebugOrErrorLogger ¶ added in v0.1.9
func NewDebugOrErrorLogger() *DebugOrErrorLogger
NewDebugOrErrorLogger initializes a new DebugOrErrorLogger with a logger that writes to os.Stderr with the standard log flags.
Returns:
*DebugOrErrorLogger: A pointer to a newly created DebugOrErrorLogger.
func (*DebugOrErrorLogger) Debug ¶ added in v0.1.9
func (l *DebugOrErrorLogger) Debug(format string, v ...interface{})
Debug logs a formatted debug message if the DEBUG_MODE environment variable is set to "true". It behaves like Printf and allows for formatted messages.
Parameters:
format string: The format string for the debug message. v ...interface{}: The values to be formatted according to the format string.
TODO: Add a DEBUG_MODE constant to the terminal package and use it here.
func (*DebugOrErrorLogger) Error ¶ added in v0.1.9
func (l *DebugOrErrorLogger) Error(format string, v ...interface{})
Error logs a formatted error message in red color to signify error conditions. It behaves like Println and allows for formatted messages.
Parameters:
format string: The format string for the error message. v ...interface{}: The values to be formatted according to the format string.
func (*DebugOrErrorLogger) RecoverFromPanic ¶ added in v0.2.2
func (l *DebugOrErrorLogger) RecoverFromPanic()
RecoverFromPanic should be deferred at the beginning of a function or goroutine to handle any panics that may occur. It logs the panic information with a colorized output to distinguish the log message clearly in the terminal.
The message "Recovered from panic:" is displayed in green, followed by the panic value in red. This method ensures that the panic does not cause the program to crash and provides a clear indication in the logs that a panic was caught and handled.
Usage:
func someFunction() { logger := terminal.NewDebugOrErrorLogger() defer logger.RecoverFromPanic() // ... function logic that might panic ... }
It is essential to call this method using defer right after obtaining a logger instance. This ensures that it can catch and handle panics from anywhere within the scope of the function or goroutine.
type GitHubRelease ¶ added in v0.2.1
type GitHubRelease struct { TagName string `json:"tag_name"` // The tag associated with the release, e.g., "v1.2.3" Name string `json:"name"` // The official name of the release Body string `json:"body"` // Detailed description or changelog for the release }
GitHubRelease represents the metadata of a software release from GitHub. It includes information such as the tag name, release name, and a description body, typically containing the changelog or release notes.
func GetFullReleaseInfo ¶ added in v0.2.1
func GetFullReleaseInfo(tagName string) (release *GitHubRelease, err error)
GetFullReleaseInfo retrieves detailed information about a specific release from GitHub. It constructs the request URL based on the provided tag name and fetches the data from the GitHub API.
Parameters:
tagName: The name of the tag for which release information is requested.
Returns:
release *GitHubRelease: A pointer to the GitHubRelease struct containing the release information. err error: An error if the request fails or if there is an issue parsing the response.
type NewLineChar ¶ added in v0.3.0
type NewLineChar struct {
NewLineChars rune
}
NewLineChar is a struct that containt Rune for New Line Character
type Session ¶
type Session struct { Client *genai.Client // Client is the generative AI client used to communicate with the AI model. ChatHistory ChatHistory // ChatHistory stores the history of the chat session. Ctx context.Context // Ctx is the context governing the session, used for cancellation. Cancel context.CancelFunc // Cancel is a function to cancel the context, used for cleanup. Ended bool // Ended indicates whether the session has ended. // contains filtered or unexported fields }
Session encapsulates the state and functionality for a chat session with a generative AI model. It holds the AI client, chat history, and context for managing the session lifecycle.
func NewSession ¶
NewSession creates a new chat session with the provided API key for authentication. It initializes the generative AI client and sets up a context for managing the session.
Parameters:
apiKey string: The API key used for authenticating requests to the AI service.
Returns:
*Session: A pointer to the newly created Session object. error: An error object if initialization fails.
func (*Session) HasEnded ¶ added in v0.2.2
HasEnded reports whether the chat session has ended. It can be called at any point to check the session's state without altering it.
Return value:
ended bool: A boolean indicating true if the session has ended, or false if it is still active.
TODO: Utilize this in multiple goroutines, such as for task queues, terminal control, etc.
func (*Session) RenewSession ¶ added in v0.2.2
RenewSession attempts to renew the client session with the AI service by reinitializing the genai.Client with the provided API key. This method is useful when the existing client session has expired or is no longer valid and a new session needs to be established to continue communication with the AI service.
The method ensures thread-safe access by using a mutex lock during the client reinitialization process. If a client session already exists, it is properly closed and a new client is created.
Parameters:
apiKey string: The API key used for authenticating requests to the AI service.
Returns:
error: An error object if reinitializing the client fails. If the operation is successful, the error is nil.
Upon successful completion, the Session's Client field is updated to reference the new genai.Client instance. In case of failure, an error is returned and the Client field is set to nil.
func (*Session) Start ¶
func (s *Session) Start()
Start begins the chat session, managing user input and AI responses. It sets up a signal listener for graceful shutdown and enters a loop to read user input and fetch AI responses indefinitely until an interrupt signal is received.
This method handles user input errors and AI communication errors by logging them and exiting. It ensures resources are cleaned up properly on exit by deferring the cancellation of the session's context and the closure of the AI client.
type TypingChars ¶ added in v0.3.0
type TypingChars struct {
AnimatedChars string
}
TypingChars is a struct that contains the Animated Chars used to print the typing effect.