diff --git a/cmd/ai.go b/cmd/ai.go index 0a55934..1e7f192 100644 --- a/cmd/ai.go +++ b/cmd/ai.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "io/ioutil" "os" "path/filepath" "strings" @@ -16,8 +17,9 @@ import ( ) var ( - apiToken string - configPath = filepath.Join(os.Getenv("HOME"), ".spaceone", "config") + apiToken string + configPath = filepath.Join(os.Getenv("HOME"), ".spaceone", "config") + resourceDir = filepath.Join(os.Getenv("HOME"), ".spaceone", "training_data") // 학습 전용 디렉터리 경로 ) // aiCmd represents the ai command @@ -68,15 +70,46 @@ var aiConfigCmd = &cobra.Command{ }, } +// aiChatCmd represents the ai chat command +var aiChatCmd = &cobra.Command{ + Use: "chat", + Short: "Ask questions about project resources", + Run: func(cmd *cobra.Command, args []string) { + // Use the query flag instead of args[0] + chat, err := cmd.Flags().GetString("query") + if err != nil || chat == "" { + pterm.Error.Println("Please provide a query with the -q flag.") + return + } + + // Load resources context from directory + contextData, err := loadAPIResourcesContext(resourceDir) + if err != nil { + pterm.Error.Println("Failed to load resources context:", err) + return + } + + // Call AI function with query and context + result, err := queryAIWithContext(chat, contextData) + if err != nil { + pterm.Error.Println("Error querying AI:", err) + return + } + + pterm.Info.Println("AI Response:", result) + }, +} + // runAIWithOpenAI processes input with OpenAI's streaming API func runAIWithOpenAI(input string, natural bool) (string, error) { // Load the API key from config if it's not set in the environment apiToken = os.Getenv("OPENAI_API_TOKEN") if apiToken == "" { - apiToken, _ = readAPIKeyFromConfig() - } - if apiToken == "" { - return "", errors.New("OpenAI API key is not set. Run `cfctl ai config` to configure it.") + var err error + apiToken, err = readAPIKeyFromConfig() + if err != nil { + return "", errors.New("OpenAI API key is not set. Run `cfctl ai config` to configure it.") + } } client := openai.NewClient(apiToken) @@ -132,7 +165,7 @@ func saveAPIKeyToConfig(apiKey string) error { // Check if OPENAI_SECRET_KEY is already present if strings.Contains(content, "OPENAI_SECRET_KEY=") { - // Update the existing key + // Update the existing key without adding an extra `=` character content = strings.ReplaceAll(content, "OPENAI_SECRET_KEY="+getAPIKeyFromContent(content), "OPENAI_SECRET_KEY="+apiKey) @@ -157,29 +190,89 @@ func getAPIKeyFromContent(content string) string { return "" } -// readAPIKeyFromConfig reads the OpenAI API key from the config file +// readAPIKeyFromConfig reads the OpenAI API key from the config file and sets it to apiToken func readAPIKeyFromConfig() (string, error) { file, err := os.Open(configPath) if err != nil { - return "", err + return "", fmt.Errorf("failed to open config file: %v", err) } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() - if len(line) > 0 && line[0:17] == "OPENAI_SECRET_KEY=" { - return line[17:], nil + line = strings.TrimSpace(line) // 공백 제거 + if strings.HasPrefix(line, "OPENAI_SECRET_KEY=") { + apiToken = strings.TrimPrefix(line, "OPENAI_SECRET_KEY=") + apiToken = strings.TrimSpace(apiToken) + return apiToken, nil } } return "", errors.New("API key not found in config file") } +// loadAPIResourcesContext loads all files in the given directory and concatenates their content as context +func loadAPIResourcesContext(dirPath string) (string, error) { + files, err := ioutil.ReadDir(dirPath) + if err != nil { + return "", fmt.Errorf("failed to read resources directory: %v", err) + } + + var contentBuilder strings.Builder + for _, file := range files { + if !file.IsDir() { + filePath := filepath.Join(dirPath, file.Name()) + data, err := ioutil.ReadFile(filePath) + if err != nil { + return "", fmt.Errorf("failed to read file %s: %v", filePath, err) + } + contentBuilder.WriteString(string(data) + "\n") + } + } + + return contentBuilder.String(), nil +} + +// queryAIWithContext queries the OpenAI API with a specific context and user query +func queryAIWithContext(query, contextData string) (string, error) { + apiToken = os.Getenv("OPENAI_API_TOKEN") + if apiToken == "" { + apiToken, _ = readAPIKeyFromConfig() + } + if apiToken == "" { + return "", errors.New("OpenAI API token is not set. Run `cfctl ai config` to configure it.") + } + + client := openai.NewClient(apiToken) + ctx := context.Background() + + // Prompt with context for AI model + prompt := fmt.Sprintf("Context: %s\n\nQuestion: %s\nAnswer:", contextData, query) + + req := openai.CompletionRequest{ + Model: openai.GPT3Babbage002, + MaxTokens: 5, // Adjust as needed + Prompt: prompt, + Stream: false, + } + + resp, err := client.CreateCompletion(ctx, req) + if err != nil { + return "", fmt.Errorf("AI query error: %v", err) + } + + // Return the AI's response text + return strings.TrimSpace(resp.Choices[0].Text), nil +} + func init() { rootCmd.AddCommand(aiCmd) aiCmd.Flags().String("input", "", "Input text for the AI to process") aiCmd.Flags().BoolP("natural", "n", false, "Enable natural language mode for the AI") + aiChatCmd.Flags().StringP("query", "q", "", "Query text for the AI to process") + aiChatCmd.MarkFlagRequired("query") // Add config command as a subcommand to aiCmd aiCmd.AddCommand(aiConfigCmd) + aiCmd.AddCommand(aiChatCmd) } diff --git a/training/api-resources.txt b/training/api-resources.txt new file mode 100644 index 0000000..ae612a3 --- /dev/null +++ b/training/api-resources.txt @@ -0,0 +1,117 @@ +Service | Resource | Short Names | Verb +board | Health | | Check, Watch +board | Post | | create, update, send, delete, get, list, stat +board | ServerInfo | | get_version +config | Health | | Check, Watch +config | ServerInfo | | get_version +config | UserConfig | | create, update, set, delete, get, list, stat +config | PublicConfig | | create, update, delete, get, list, stat +config | DomainConfig | | create, update, set, delete, get, list, stat +cost_analysis | ServerInfo | | get_version +cost_analysis | Job | | cancel, get, list, stat +cost_analysis | Cost | | create, delete, get, list, analyze, stat +cost_analysis | Budget | | create, update, set_notification, delete, get, list, stat +cost_analysis | Health | | Check, Watch +cost_analysis | CostQuerySet | | create, update, delete, get, list, stat +cost_analysis | CostReport | | send, get_url, get, list, stat +cost_analysis | CostReportConfig | | create, update, update_recipients, enable, disable, delete, run, get, list, stat +cost_analysis | CostReportData | | list, analyze, stat +cost_analysis | DataSource | | register, update, update_permissions, update_plugin, update_secret_data, verify_plugin, enable, disable, deregister, sync, get, list, stat +cost_analysis | DataSourceAccount | | update, reset, get, list, analyze, stat +cost_analysis | BudgetUsage | | list, analyze, stat +cost_analysis | DataSourceRule | | create, update, change_order, delete, get, list, stat +cost_analysis | UnifiedCost | | get, list, analyze, stat +cost_analysis | JobTask | | get, list, stat +dashboard | PrivateFolder | | create, update, delete, get, list, stat +dashboard | PublicWidget | | create, update, delete, load, get, list +dashboard | PublicFolder | | create, update, share, unshare, delete, get, list, stat +dashboard | PublicDataTable | | add, transform, update, delete, load, get, list +dashboard | PublicDashboard | | create, update, change_folder, share, unshare, delete, get, list, stat +dashboard | PrivateWidget | | create, update, delete, load, get, list +dashboard | Health | | Check, Watch +dashboard | PrivateDataTable | | add, transform, update, delete, load, get, list +dashboard | PrivateDashboard | | create, update, change_folder, delete, get, list, stat +dashboard | ServerInfo | | get_version +file_manager | Health | | Check, Watch +file_manager | File | | add, update, delete, get_download_url, get, list, stat +file_manager | ServerInfo | | get_version +identity | RoleBinding | | create, update_role, delete, get, list, stat +identity | ServerInfo | | get_version +identity | Agent | | create, enable, disable, regenerate, delete, get, list +identity | App | | create, update, generate_client_secret, enable, disable, delete, get, check, list, stat +identity | Domain | | create, update, delete, enable, disable, get, get_auth_info, get_public_key, list, stat +identity | Endpoint | | list +identity | ExternalAuth | | set, unset, get +identity | Job | | delete, get, list, stat +identity | Project | | create, update, update_project_type, change_project_group, delete, add_users, remove_users, get, list, stat +identity | ProjectGroup | | create, update, change_parent_group, delete, add_users, remove_users, get, list, stat +identity | Health | | Check, Watch +identity | Provider | | create, update, update_plugin, delete, get, list, stat +identity | Role | | create, update, enable, disable, delete, get, list, list_basic_role, stat +identity | System | | init +identity | Schema | | create, update, delete, get, list, stat +identity | ServiceAccount | | create, update, update_secret_data, delete_secret_data, delete, get, list, stat +identity | Token | it | issue, grant +identity | TrustedAccount | | create, update, update_secret_data, delete, sync, get, list, stat +identity | User | iu | create, update, verify_email, disable_mfa, set_required_actions, set_refresh_timeout, enable, disable, delete, get, list, stat +identity | UserGroup | | create, update, delete, add_users, remove_users, get, list, stat +identity | UserProfile | | update, verify_email, confirm_email, reset_password, enable_mfa, disable_mfa, confirm_mfa, get, get_workspaces, get_workspace_groups +identity | Workspace | | create, update, change_workspace_group, delete, enable, disable, get, check, list, stat +identity | WorkspaceGroup | | create, update, delete, add_users, remove_users, update_role, get, list, stat +identity | WorkspaceGroupUser | | add, remove, update_role, find, get, list, stat +identity | WorkspaceUser | | create, get, find, list, stat +inventory | MetricExample | | create, update, delete, get, list, stat +inventory | Note | | create, update, delete, get, list, stat +inventory | Region | | create, update, delete, get, list, stat +inventory | MetricData | | list, stat, analyze +inventory | Namespace | | create, update, delete, get, list, stat +inventory | Metric | | create, update, delete, run, test, get, list, stat +inventory | JobTask | | delete, get, list, stat +inventory | Job | | delete, get, list, analyze, stat +inventory | CollectorRule | | create, update, change_order, delete, get, list, stat +inventory | Collector | | create, update, update_plugin, verify_plugin, delete, get, list, stat, collect +inventory | CloudServiceType | | create, update, delete, get, list, stat +inventory | CloudServiceStats | | list, analyze, stat +inventory | Health | | Check, Watch +inventory | ServerInfo | | get_version +inventory | ChangeHistory | | list, stat +inventory | CloudService | | create, update, delete, get, list, export, analyze, stat +inventory | CloudServiceQuerySet | | create, update, delete, run, test, enable, disable, get, list, stat +inventory | CloudServiceReport | | create, update, delete, send, get, list, stat +monitoring | Health | | Check, Watch +monitoring | DataSource | | register, update, enable, disable, deregister, update_plugin, verify_plugin, get, list, stat +monitoring | EscalationPolicy | | create, update, set_default, delete, get, list, stat +monitoring | Event | | create, get, list, stat +monitoring | EventRule | | create, update, change_order, delete, get, list, stat +monitoring | Log | | list +monitoring | Metric | | list, get_data +monitoring | ServerInfo | | get_version +monitoring | Alert | | create, update, assign_user, update_state, delete, get, list, stat +monitoring | Note | | create, update, delete, get, list, stat +monitoring | ProjectAlertConfig | | create, update, delete, get, list, stat +monitoring | Webhook | | create, update, update_plugin, verify_plugin, enable, disable, delete, get, list, stat +notification | Health | | Check, Watch +notification | Protocol | | create, update, update_plugin, enable, disable, delete, get, list, stat +notification | ProjectChannel | | create, update, set_schedule, set_subscription, enable, disable, delete, get, list, stat +notification | Notification | | create, push, delete, set_read, get, list, stat +notification | ServerInfo | | get_version +notification | UserChannel | | create, update, set_schedule, set_subscription, enable, disable, delete, get, list, stat +plugin | Supervisor | | publish, register, update, deregister, enable, disable, recover_plugin, get, list, stat, list_plugins +plugin | Health | | Check, Watch +plugin | ServerInfo | | get_version +plugin | Plugin | | get_plugin_endpoint, get_plugin_metadata, notify_failure +repository | Health | | Check, Watch +repository | ServerInfo | | get_version +repository | Plugin | rp | register, update, deregister, enable, disable, get_versions, get, list +repository | DashboardTemplate | | register, update, deregister, enable, disable, get, list +repository | Repository | | list +secret | TrustedSecret | | create, update, delete, update_data, get_data, get, list, stat +secret | Health | | Check, Watch +secret | ServerInfo | | get_version +secret | Secret | | create, update, delete, enable, disable, update_data, get_data, get, list, stat +secret | UserSecret | | create, update, delete, update_data, get_data, get, list, stat +statistics | Schedule | | add, update, enable, disable, delete, get, list, stat +statistics | Health | | Check, Watch +statistics | ServerInfo | | get_version +statistics | History | | create, list, stat +statistics | Resource | | stat