Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6102f905ee | |||
| ba49f82953 | |||
| 55c1b180f7 |
@@ -0,0 +1,3 @@
|
|||||||
|
OPENAI_API_KEY=ollama
|
||||||
|
OLLAMA_BASE_URL=http://192.168.0.120:11434/v1/
|
||||||
|
OLLAMA_MODEL=gpt-oss:120b
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
OPENAI_API_KEY=ollama
|
||||||
|
OLLAMA_BASE_URL=http://192.168.0.120:11434/v1/
|
||||||
|
OLLAMA_MODEL=gpt-oss:120b
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import os
|
||||||
|
from typing import AsyncGenerator
|
||||||
|
from openai import AsyncOpenAI
|
||||||
|
import json
|
||||||
|
|
||||||
|
api_key = os.getenv('OPENAI_API_KEY', 'ollama')
|
||||||
|
base_url = os.getenv('OLLAMA_BASE_URL', 'http://192.168.0.120:11434/v1/')
|
||||||
|
model = os.getenv('OLLAMA_MODEL', 'gpt-oss:120b')
|
||||||
|
|
||||||
|
print(f"[LLM] API key configured: {'Yes' if api_key else 'No'}")
|
||||||
|
print(f"[LLM] Base URL: {base_url}")
|
||||||
|
print(f"[LLM] Model: {model}")
|
||||||
|
|
||||||
|
client = AsyncOpenAI(api_key=api_key, base_url=base_url)
|
||||||
|
|
||||||
|
async def stream_openai(prompt: str) -> AsyncGenerator[str, None]:
|
||||||
|
"""
|
||||||
|
调用 OpenAI/Ollama API 并流式返回补全内容。
|
||||||
|
参考 completions-sample-code 的 streaming 逻辑。
|
||||||
|
"""
|
||||||
|
print(f"[LLM] Calling API with prompt length: {len(prompt)}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
stream = await client.chat.completions.create(
|
||||||
|
model=model,
|
||||||
|
messages=[{"role": "user", "content": prompt}],
|
||||||
|
stream=True,
|
||||||
|
max_tokens=128,
|
||||||
|
temperature=0.2,
|
||||||
|
)
|
||||||
|
|
||||||
|
chunk_count = 0
|
||||||
|
async for chunk in stream:
|
||||||
|
if chunk.choices[0].delta.content:
|
||||||
|
content = chunk.choices[0].delta.content
|
||||||
|
chunk_count += 1
|
||||||
|
print(f"[LLM] Chunk {chunk_count}: {content}")
|
||||||
|
yield json.dumps({"content": content})
|
||||||
|
|
||||||
|
print(f"[LLM] Stream complete, total chunks: {chunk_count}")
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Error: {str(e)}"
|
||||||
|
print(f"[LLM] Error: {error_msg}")
|
||||||
|
yield json.dumps({"error": str(e)})
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
from fastapi.responses import StreamingResponse
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
class CompletionRequest(BaseModel):
|
||||||
|
prefix: str
|
||||||
|
suffix: str
|
||||||
|
languageId: str = 'markdown'
|
||||||
|
|
||||||
|
def generate_stream(request: CompletionRequest):
|
||||||
|
from prompt import build_prompt
|
||||||
|
from llm import stream_openai
|
||||||
|
|
||||||
|
print(f"[Backend] Received request - prefix length: {len(request.prefix)}, suffix length: {len(request.suffix)}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
prompt = build_prompt(request.prefix, request.suffix)
|
||||||
|
print(f"[Backend] Built prompt (first 100 chars): {prompt[:100]}...")
|
||||||
|
|
||||||
|
async def gen():
|
||||||
|
chunk_count = 0
|
||||||
|
async for chunk in stream_openai(prompt):
|
||||||
|
chunk_count += 1
|
||||||
|
yield f"data: {chunk}\n\n"
|
||||||
|
if chunk_count % 5 == 0:
|
||||||
|
print(f"[Backend] Sent chunk {chunk_count}")
|
||||||
|
yield "data: {\"done\": true}\n\n"
|
||||||
|
print(f"[Backend] Stream complete, total chunks: {chunk_count}")
|
||||||
|
return gen()
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"{{\"error\": \"{str(e)}\"}}"
|
||||||
|
print(f"[Backend] Error: {e}")
|
||||||
|
yield f"data: {error_msg}\n\n"
|
||||||
|
|
||||||
|
@app.post("/v1/completions")
|
||||||
|
async def create_completion(request: CompletionRequest):
|
||||||
|
print(f"[Backend] POST /v1/completions called")
|
||||||
|
return StreamingResponse(generate_stream(request), media_type="text/event-stream")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
print("[Backend] Starting server on http://0.0.0.0:8000")
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import os
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
def build_prompt(prefix: str, suffix: str) -> str:
|
||||||
|
"""
|
||||||
|
构建用于代码补全的 Prompt。
|
||||||
|
参考 completions-sample-code 的 extractPrompt 逻辑简化实现。
|
||||||
|
"""
|
||||||
|
MAX_CONTEXT_LINES = 30
|
||||||
|
|
||||||
|
prefix_lines = prefix.split('\n')
|
||||||
|
suffix_lines = suffix.split('\n') if suffix else []
|
||||||
|
|
||||||
|
recent_prefix = '\n'.join(prefix_lines[-MAX_CONTEXT_LINES:])
|
||||||
|
recent_suffix = '\n'.join(suffix_lines[:5])
|
||||||
|
|
||||||
|
prompt = f"""
|
||||||
|
You are a helpful writing assistant. Continue the text naturally based on the context.
|
||||||
|
|
||||||
|
Context (before cursor):
|
||||||
|
{recent_prefix}
|
||||||
|
|
||||||
|
Complete this:
|
||||||
|
{suffix if suffix else '(cursor here)'}
|
||||||
|
|
||||||
|
Continue:"""
|
||||||
|
|
||||||
|
return prompt.strip()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fastapi
|
||||||
|
uvicorn
|
||||||
|
openai
|
||||||
|
pydantic
|
||||||
|
python-dotenv
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { InlineEditRequestLogContext } from '../../../platform/inlineEdits/common/inlineEditLogContext';
|
|
||||||
import { basename } from '../../../util/vs/base/common/path';
|
|
||||||
|
|
||||||
export class GhostTextContext extends InlineEditRequestLogContext {
|
|
||||||
override getDebugName(): string {
|
|
||||||
return `Ghost | ${basename(this.filePath)} (v${this.version})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user