Skip to content

Commit 7214bef

Browse files
committed
feat: enhance LLM functionality with nginx configuration context and update ESLint auto-imports
1 parent c355cb8 commit 7214bef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+5664
-3628
lines changed

.claude/settings.local.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"mcp__context7__get-library-docs",
1515
"Bash(find:*)",
1616
"Bash(sed:*)",
17-
"Bash(cp:*)"
17+
"Bash(cp:*)",
18+
"mcp__eslint__lint-files"
1819
],
1920
"deny": []
2021
}

api/llm/llm.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,51 @@ import (
1717
"github.com/uozi-tech/cosy/logger"
1818
)
1919

20-
const LLMInitPrompt = `You are a assistant who can help users write and optimise the configurations of Nginx,
21-
the first user message contains the content of the configuration file which is currently opened by the user and
22-
the current language code(CLC). You suppose to use the language corresponding to the CLC to give the first reply.
23-
Later the language environment depends on the user message.
24-
The first reply should involve the key information of the file and ask user what can you help them.`
2520

2621
func MakeChatCompletionRequest(c *gin.Context) {
2722
var json struct {
28-
Filepath string `json:"filepath"`
29-
Messages []openai.ChatCompletionMessage `json:"messages"`
23+
Type string `json:"type"`
24+
Messages []openai.ChatCompletionMessage `json:"messages"`
25+
Language string `json:"language,omitempty"`
26+
NginxConfig string `json:"nginx_config,omitempty"` // Separate field for nginx configuration content
3027
}
3128

3229
if !cosy.BindAndValid(c, &json) {
3330
return
3431
}
3532

33+
// Choose appropriate system prompt based on the type
34+
var systemPrompt string
35+
if json.Type == "terminal" {
36+
systemPrompt = llm.TerminalAssistantPrompt
37+
} else {
38+
systemPrompt = llm.NginxConfigPrompt
39+
}
40+
41+
// Append language instruction if language is provided
42+
if json.Language != "" {
43+
systemPrompt += fmt.Sprintf("\n\nIMPORTANT: Please respond in the language corresponding to this language code: %s", json.Language)
44+
}
45+
3646
messages := []openai.ChatCompletionMessage{
3747
{
3848
Role: openai.ChatMessageRoleSystem,
39-
Content: LLMInitPrompt,
49+
Content: systemPrompt,
4050
},
4151
}
4252

43-
messages = append(messages, json.Messages...)
44-
45-
if json.Filepath != "" {
46-
messages = llm.ChatCompletionWithContext(json.Filepath, messages)
53+
// Add nginx configuration context if provided
54+
if json.Type != "terminal" && json.NginxConfig != "" {
55+
// Add nginx configuration as context to the first user message
56+
if len(json.Messages) > 0 && json.Messages[0].Role == openai.ChatMessageRoleUser {
57+
// Prepend the nginx configuration to the first user message
58+
contextualContent := fmt.Sprintf("Nginx Configuration:\n```nginx\n%s\n```\n\n%s", json.NginxConfig, json.Messages[0].Content)
59+
json.Messages[0].Content = contextualContent
60+
}
4761
}
4862

63+
messages = append(messages, json.Messages...)
64+
4965
// SSE server
5066
api.SetSSEHeaders(c)
5167

api/llm/session.go

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,26 @@ import (
1515
"github.com/uozi-tech/cosy/logger"
1616
)
1717

18+
const TerminalAssistantPath = "__terminal_assistant__"
19+
1820
// GetLLMSessions returns LLM sessions with optional filtering
1921
func GetLLMSessions(c *gin.Context) {
2022
g := query.LLMSession
2123
query := g.Order(g.UpdatedAt.Desc())
2224

23-
// Filter by path if provided
24-
if path := c.Query("path"); path != "" {
25-
if !helper.IsUnderDirectory(path, nginx.GetConfPath()) {
25+
// Filter by type if provided
26+
if assistantType := c.Query("type"); assistantType != "" {
27+
if assistantType == "terminal" {
28+
// For terminal type, filter by terminal assistant path
29+
query = query.Where(g.Path.Eq(TerminalAssistantPath))
30+
} else if assistantType == "nginx" {
31+
// For nginx type, exclude terminal assistant path
32+
query = query.Where(g.Path.Neq(TerminalAssistantPath))
33+
}
34+
} else if path := c.Query("path"); path != "" {
35+
// Filter by path if provided (legacy support)
36+
// Skip path validation for terminal assistant
37+
if path != TerminalAssistantPath && !helper.IsUnderDirectory(path, nginx.GetConfPath()) {
2638
c.JSON(http.StatusForbidden, gin.H{
2739
"message": "path is not under the nginx conf path",
2840
})
@@ -59,23 +71,31 @@ func CreateLLMSession(c *gin.Context) {
5971
var json struct {
6072
Title string `json:"title" binding:"required"`
6173
Path string `json:"path"`
74+
Type string `json:"type"`
6275
}
6376

6477
if !cosy.BindAndValid(c, &json) {
6578
return
6679
}
6780

68-
// Validate path if provided
69-
if json.Path != "" && !helper.IsUnderDirectory(json.Path, nginx.GetConfPath()) {
70-
c.JSON(http.StatusForbidden, gin.H{
71-
"message": "path is not under the nginx conf path",
72-
})
73-
return
81+
// Determine path based on type
82+
var sessionPath string
83+
if json.Type == "terminal" {
84+
sessionPath = TerminalAssistantPath
85+
} else {
86+
sessionPath = json.Path
87+
// Validate path for non-terminal types
88+
if sessionPath != "" && !helper.IsUnderDirectory(sessionPath, nginx.GetConfPath()) {
89+
c.JSON(http.StatusForbidden, gin.H{
90+
"message": "path is not under the nginx conf path",
91+
})
92+
return
93+
}
7494
}
7595

7696
session := &model.LLMSession{
7797
Title: json.Title,
78-
Path: json.Path,
98+
Path: sessionPath,
7999
Messages: []openai.ChatCompletionMessage{},
80100
MessageCount: 0,
81101
IsActive: true,
@@ -197,7 +217,8 @@ func DuplicateLLMSession(c *gin.Context) {
197217
func GetLLMSessionByPath(c *gin.Context) {
198218
path := c.Query("path")
199219

200-
if !helper.IsUnderDirectory(path, nginx.GetConfPath()) {
220+
// Skip path validation for terminal assistant
221+
if path != TerminalAssistantPath && !helper.IsUnderDirectory(path, nginx.GetConfPath()) {
201222
c.JSON(http.StatusForbidden, gin.H{
202223
"message": "path is not under the nginx conf path",
203224
})
@@ -250,7 +271,8 @@ func CreateOrUpdateLLMSessionByPath(c *gin.Context) {
250271
return
251272
}
252273

253-
if !helper.IsUnderDirectory(json.FileName, nginx.GetConfPath()) {
274+
// Skip path validation for terminal assistant
275+
if json.FileName != TerminalAssistantPath && !helper.IsUnderDirectory(json.FileName, nginx.GetConfPath()) {
254276
c.JSON(http.StatusForbidden, gin.H{
255277
"message": "path is not under the nginx conf path",
256278
})

app/.eslint-auto-import.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export default {
44
"$ngettext": true,
55
"$npgettext": true,
66
"$pgettext": true,
7+
"App": true,
78
"Component": true,
89
"ComponentPublicInstance": true,
910
"ComputedRef": true,

app/auto-imports.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ declare global {
1010
const $ngettext: typeof import('@/gettext')['$ngettext']
1111
const $npgettext: typeof import('@/gettext')['$npgettext']
1212
const $pgettext: typeof import('@/gettext')['$pgettext']
13+
const App: typeof import('ant-design-vue')['App']
1314
const EffectScope: typeof import('vue')['EffectScope']
1415
const T: typeof import('@/language')['T']
1516
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
@@ -38,7 +39,10 @@ declare global {
3839
const mapStores: typeof import('pinia')['mapStores']
3940
const mapWritableState: typeof import('pinia')['mapWritableState']
4041
const markRaw: typeof import('vue')['markRaw']
42+
const message: typeof import('@/useAntApp')['message']
43+
const modal: typeof import('@/useAntApp')['modal']
4144
const nextTick: typeof import('vue')['nextTick']
45+
const notification: typeof import('@/useAntApp')['notification']
4246
const onActivated: typeof import('vue')['onActivated']
4347
const onBeforeMount: typeof import('vue')['onBeforeMount']
4448
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
@@ -72,6 +76,9 @@ declare global {
7276
const toValue: typeof import('vue')['toValue']
7377
const triggerRef: typeof import('vue')['triggerRef']
7478
const unref: typeof import('vue')['unref']
79+
const useAntAppStore: typeof import('@/pinia')['useAntAppStore']
80+
const useApp: typeof import('ant-design-vue')['App.useApp']
81+
const useAppUtils: typeof import('@/useAntApp')['useAppUtils']
7582
const useAttrs: typeof import('vue')['useAttrs']
7683
const useCssModule: typeof import('vue')['useCssModule']
7784
const useCssVars: typeof import('vue')['useCssVars']
@@ -103,6 +110,7 @@ declare module 'vue' {
103110
readonly $ngettext: UnwrapRef<typeof import('@/gettext')['$ngettext']>
104111
readonly $npgettext: UnwrapRef<typeof import('@/gettext')['$npgettext']>
105112
readonly $pgettext: UnwrapRef<typeof import('@/gettext')['$pgettext']>
113+
readonly App: UnwrapRef<typeof import('ant-design-vue')['App']>
106114
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
107115
readonly T: UnwrapRef<typeof import('@/language')['T']>
108116
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>

app/components.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export {}
99
declare module 'vue' {
1010
export interface GlobalComponents {
1111
AAlert: typeof import('ant-design-vue/es')['Alert']
12+
AApp: typeof import('ant-design-vue/es')['App']
1213
AAutoComplete: typeof import('ant-design-vue/es')['AutoComplete']
1314
AAvatar: typeof import('ant-design-vue/es')['Avatar']
1415
ABadge: typeof import('ant-design-vue/es')['Badge']
@@ -90,6 +91,7 @@ declare module 'vue' {
9091
LLMChatMessageInput: typeof import('./src/components/LLM/ChatMessageInput.vue')['default']
9192
LLMChatMessageList: typeof import('./src/components/LLM/ChatMessageList.vue')['default']
9293
LLMLLM: typeof import('./src/components/LLM/LLM.vue')['default']
94+
LLMLLMIframe: typeof import('./src/components/LLM/LLMIframe.vue')['default']
9395
LLMLLMSessionSelector: typeof import('./src/components/LLM/LLMSessionSelector.vue')['default']
9496
LLMLLMSessionSidebar: typeof import('./src/components/LLM/LLMSessionSidebar.vue')['default']
9597
LLMLLMSessionTabs: typeof import('./src/components/LLM/LLMSessionTabs.vue')['default']
@@ -123,6 +125,7 @@ declare module 'vue' {
123125
SelfCheckSelfCheckHeaderBanner: typeof import('./src/components/SelfCheck/SelfCheckHeaderBanner.vue')['default']
124126
SensitiveStringSensitiveString: typeof import('./src/components/SensitiveString/SensitiveString.vue')['default']
125127
SetLanguageSetLanguage: typeof import('./src/components/SetLanguage/SetLanguage.vue')['default']
128+
ShadowRootShadowRoot: typeof import('./src/components/ShadowRoot/ShadowRoot.vue')['default']
126129
SwitchAppearanceIconsVPIconMoon: typeof import('./src/components/SwitchAppearance/icons/VPIconMoon.vue')['default']
127130
SwitchAppearanceIconsVPIconSun: typeof import('./src/components/SwitchAppearance/icons/VPIconSun.vue')['default']
128131
SwitchAppearanceSwitchAppearance: typeof import('./src/components/SwitchAppearance/SwitchAppearance.vue')['default']

app/src/App.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ loadTranslations(route)
5959
:locale="lang"
6060
:auto-insert-space-in-button="false"
6161
>
62-
<RouterView />
62+
<AApp>
63+
<RouterView />
64+
</AApp>
6365
</AConfigProvider>
6466
</template>
6567

app/src/api/llm.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,24 @@ const llm = {
4949
},
5050

5151
// Session APIs
52-
get_sessions(path?: string) {
52+
get_sessions(pathOrType?: string, isType?: boolean) {
53+
const params: Record<string, string> = {}
54+
if (pathOrType) {
55+
if (isType) {
56+
params.type = pathOrType
57+
}
58+
else {
59+
params.path = pathOrType
60+
}
61+
}
5362
return http.get<LLMSessionResponse[]>('/llm_sessions', {
54-
params: path ? { path } : undefined,
63+
params: Object.keys(params).length > 0 ? params : undefined,
5564
})
5665
},
5766
get_session(sessionId: string) {
5867
return http.get<LLMSessionResponse>(`/llm_sessions/${sessionId}`)
5968
},
60-
create_session(data: { title: string, path?: string }) {
69+
create_session(data: { title: string, path?: string, type?: string }) {
6170
return http.post<LLMSessionResponse>('/llm_sessions', data)
6271
},
6372
update_session(sessionId: string, data: { title?: string, messages?: ChatComplicationMessage[] }) {

0 commit comments

Comments
 (0)