Skip to content

Commit 4aa0423

Browse files
committed
implemented ai configered is available
1 parent cb7af6f commit 4aa0423

3 files changed

Lines changed: 83 additions & 11 deletions

File tree

docs/DIAGRAM.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#### TERMINAL INTELLIGENCE CLI Diagram (Markdown Plain Ascii)
22

3-
┌───────────────────────────────────────────────────────────────────────────────┐ | 01010100 1001001 TERMINAL INTELLIGENCE (TI) Build: 0017 │
3+
┌───────────────────────────────────────────────────────────────────────────────┐ | 01010100 1001001 TERMINAL INTELLIGENCE (TI) Build: 0017 │
44
└───────────────────────────────────────────────────────────────────────────────┘
55
┌───────────────────────────────────────┐ ┌─────────────────────────────────────┐
6-
│ Editor: <file name> │ │ ti>
6+
│ Editor: <file name> │ │ ai-assist>
77
└───────────────────────────────────────┘ │ ⚡ provider/model ✓ ready │
88
┌───────────────────────────────────────┐ └─────────────────────────────────────┘
99
│ 001 | │ ┌─────────────────────────────────────┐

internal/ui/aichat.go

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ type AIChatPane struct {
7070
terminalOutput []string // Output lines from terminal execution
7171
stdinWriter io.WriteCloser // Stdin pipe for sending input to running command
7272
terminalInput string // Current input line being typed in terminal mode
73+
aiAvailable bool // Whether the AI service is reachable
74+
aiChecked bool // Whether the availability check has completed
7375
}
7476

7577
// AIResponseMsg is sent when AI response chunk is received.
@@ -114,6 +116,11 @@ type TerminalDoneMsg struct {
114116
Err error
115117
}
116118

119+
// AIAvailabilityMsg is sent when the AI availability check completes.
120+
type AIAvailabilityMsg struct {
121+
Available bool
122+
}
123+
117124
// NewAIChatPane creates a new AI chat pane.
118125
// Initializes an empty conversation with the specified AI client and model.
119126
// Defaults to "llama2" model if none is specified.
@@ -145,6 +152,53 @@ func NewAIChatPane(client ai.AIClient, model string, provider string) *AIChatPan
145152
}
146153
}
147154

155+
// CheckAIAvailability returns a tea.Cmd that checks if the configured AI
156+
// provider and model are reachable, sending an AIAvailabilityMsg with the result.
157+
func (a *AIChatPane) CheckAIAvailability() tea.Cmd {
158+
client := a.aiClient
159+
model := a.model
160+
provider := a.provider
161+
162+
return func() tea.Msg {
163+
if client == nil {
164+
return AIAvailabilityMsg{Available: false}
165+
}
166+
167+
// Check if the service is reachable
168+
available, err := client.IsAvailable()
169+
if err != nil || !available {
170+
return AIAvailabilityMsg{Available: false}
171+
}
172+
173+
// For Ollama, also verify the specific model exists
174+
if provider == "ollama" {
175+
models, err := client.ListModels()
176+
if err != nil {
177+
return AIAvailabilityMsg{Available: false}
178+
}
179+
found := false
180+
for _, m := range models {
181+
if m == model || m == model+":latest" {
182+
found = true
183+
break
184+
}
185+
}
186+
if !found {
187+
return AIAvailabilityMsg{Available: false}
188+
}
189+
}
190+
191+
// For Gemini, try listing models to confirm API key + model access
192+
if provider == "gemini" {
193+
// IsAvailable already checks API key; do a lightweight generate test
194+
// by just confirming the client was created with a key.
195+
// The real validation happens on first request.
196+
}
197+
198+
return AIAvailabilityMsg{Available: true}
199+
}
200+
}
201+
148202
// SetActiveArea sets the active area (0: Input, 1: Response).
149203
// Used to switch focus between input and response areas.
150204
//
@@ -383,6 +437,10 @@ func (a *AIChatPane) Update(msg tea.Msg) tea.Cmd {
383437
a.viewModeScroll = len(a.terminalOutput)
384438
}
385439
return nil
440+
case AIAvailabilityMsg:
441+
a.aiChecked = true
442+
a.aiAvailable = msg.Available
443+
return nil
386444
}
387445

388446
return nil
@@ -983,13 +1041,13 @@ func (a *AIChatPane) View() string {
9831041

9841042
// Calculate heights - input gets 2 lines (prompt + status), rest for responses
9851043
maxInputLines := 3
986-
inputWidth := a.width - 8 // Account for border, padding, and "TI> " prefix
1044+
inputWidth := a.width - 15 // Account for border, padding, and "ai-assist> " prefix
9871045
if inputWidth < 10 {
9881046
inputWidth = 10
9891047
}
9901048

9911049
// Calculate how many lines the input will actually take
992-
promptText := "TI> " + a.inputBuffer
1050+
promptText := "ai-assist> " + a.inputBuffer
9931051
if a.focused && a.activeArea == 0 {
9941052
promptText += "█" // Cursor
9951053
}
@@ -1011,13 +1069,23 @@ func (a *AIChatPane) View() string {
10111069

10121070
// Build status line to show inside the input box
10131071
statusText := "⚡ " + a.provider + "/" + a.model
1014-
if a.streaming {
1015-
statusText += " ⏳ generating..."
1016-
} else {
1072+
if !a.aiChecked {
1073+
statusText += " … checking"
1074+
} else if a.aiAvailable {
10171075
statusText += " ✓ ready"
1076+
} else {
1077+
statusText += " ✗ No AI Accessible"
1078+
}
1079+
statusColor := "15" // white
1080+
if a.aiChecked && !a.aiAvailable {
1081+
statusColor = "196" // red for unavailable
1082+
}
1083+
if a.streaming {
1084+
statusText = "⚡ " + a.provider + "/" + a.model + " ⏳ generating..."
1085+
statusColor = "15"
10181086
}
10191087
statusLine := lipgloss.NewStyle().
1020-
Foreground(lipgloss.Color("240")).
1088+
Foreground(lipgloss.Color(statusColor)).
10211089
Render(statusText)
10221090

10231091
// Add status as an extra line inside the input box

internal/ui/app.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func New(config *types.AppConfig, buildNumber string) *App {
149149
// This is part of the Bubble Tea Model interface.
150150
// Currently returns nil as no initial commands are needed.
151151
func (a *App) Init() tea.Cmd {
152-
return nil
152+
return a.aiPane.CheckAIAvailability()
153153
}
154154

155155
// Update handles messages and updates application state.
@@ -253,8 +253,12 @@ func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
253253
// Update agentic fixer
254254
a.agenticFixer = agentic.NewAgenticCodeFixer(a.aiClient, a.config.DefaultModel)
255255

256+
// Re-check AI availability with the new config
257+
a.aiPane.aiChecked = false
258+
a.aiPane.aiAvailable = false
259+
256260
a.statusMessage = "Configuration saved successfully to " + configPath
257-
return a, nil
261+
return a, a.aiPane.CheckAIAvailability()
258262

259263
case InsertCodeMsg:
260264
// Handle code insertion from AI pane
@@ -351,7 +355,7 @@ func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
351355

352356
return a, nil
353357

354-
case TerminalOutputMsg, TerminalDoneMsg, AIResponseMsg, AINotificationMsg:
358+
case TerminalOutputMsg, TerminalDoneMsg, AIResponseMsg, AINotificationMsg, AIAvailabilityMsg:
355359
cmd := a.aiPane.Update(msg)
356360
cmds = append(cmds, cmd)
357361
return a, tea.Batch(cmds...)

0 commit comments

Comments
 (0)