Skip to content
This repository was archived by the owner on Jun 5, 2025. It is now read-only.

Commit c36a53e

Browse files
Add a FIM pipeline to providers
Related: #87, #43 The PR adds a FIM pipeline independent from chat completion pipeline. It could still be faulty since we need: - Message normalizer. We now expect all messages to have the key `messages`. However, there are incoming messages with `prompt`. - Secreets detector. There's the skeleton of a class called SecretAnalyzer that is meant to analyze the messages and return a warning if it detected a secret.
1 parent 09060f1 commit c36a53e

7 files changed

Lines changed: 93 additions & 3 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from litellm import ChatCompletionRequest
2+
3+
from codegate.pipeline.base import PipelineContext, PipelineResult, PipelineStep, PipelineResponse
4+
5+
6+
class SecretAnalyzer(PipelineStep):
7+
"""Pipeline step that handles version information requests."""
8+
9+
message_blocked = """
10+
⚠️ CodeGate Security Warning! Analysis Report ⚠️
11+
Potential leak of sensitive credentials blocked
12+
13+
Recommendations:
14+
- Use environment variables for secrets
15+
"""
16+
17+
@property
18+
def name(self) -> str:
19+
"""
20+
Returns the name of this pipeline step.
21+
22+
Returns:
23+
str: The identifier 'fim-secret-analyzer'
24+
"""
25+
return "fim-secret-analyzer"
26+
27+
async def process(
28+
self,
29+
request: ChatCompletionRequest,
30+
context: PipelineContext
31+
) -> PipelineResult:
32+
# We should call here Secrets Blocking module to see if the request messages contain secrets
33+
# messages_contain_secrets = [analyze_msg_secrets(msg) for msg in request.messages]
34+
# message_with_secrets = any(messages_contain_secretes)
35+
36+
# For the moment to test shortcutting just treat all messages as if they contain secrets
37+
message_with_secrets = True
38+
if message_with_secrets:
39+
return PipelineResult(
40+
response=PipelineResponse(
41+
step_name=self.name,
42+
content=self.message_blocked,
43+
model=request["model"],
44+
),
45+
)
46+
47+
# No messages with secrets, execute the rest of the pipeline
48+
return PipelineResult(request=request)

src/codegate/providers/anthropic/provider.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
from typing import Optional
23

34
from fastapi import Header, HTTPException, Request
45

src/codegate/providers/litellmshim/litellmshim.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,11 @@ def create_streaming_response(self, stream: AsyncIterator[Any]) -> StreamingResp
4343
},
4444
status_code=200,
4545
)
46+
47+
def is_fim_request(self, data: Dict) -> bool:
48+
"""
49+
Determine from the raw incoming data if it's a FIM request.
50+
This is needed here since completion_handler is used by provider and provider
51+
doesn't know about the adapter.
52+
"""
53+
return self._adapter.is_fim_request(data)

src/codegate/providers/llamacpp/completion_handler.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,11 @@ def create_streaming_response(self, stream: Iterator[Any]) -> StreamingResponse:
6565
},
6666
status_code=200,
6767
)
68+
69+
def is_fim_request(self, data: Dict) -> bool:
70+
"""
71+
Determine from the raw incoming data if it's a FIM request.
72+
This is needed here since completion_handler is used by provider and provider
73+
doesn't know about the adapter.
74+
"""
75+
return self._adapter.is_fim_request(data)

src/codegate/providers/llamacpp/provider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import json
2+
from typing import Optional
23

34
from fastapi import Request
45

6+
from codegate.pipeline.base import SequentialPipelineProcessor
57
from codegate.providers.base import BaseProvider
68
from codegate.providers.llamacpp.completion_handler import LlamaCppCompletionHandler
79
from codegate.providers.llamacpp.normalizer import LLamaCppInputNormalizer, LLamaCppOutputNormalizer

src/codegate/providers/openai/provider.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import json
2+
from typing import Optional
23

34
from fastapi import Header, HTTPException, Request
45

6+
from codegate.pipeline.base import SequentialPipelineProcessor
57
from codegate.providers.base import BaseProvider
68
from codegate.providers.litellmshim import LiteLLmShim, sse_stream_generator
79
from codegate.providers.openai.adapter import OpenAIInputNormalizer, OpenAIOutputNormalizer
@@ -42,3 +44,18 @@ async def create_completion(
4244

4345
stream = await self.complete(data, api_key)
4446
return self._completion_handler.create_streaming_response(stream)
47+
48+
@self.router.post(f"/{self.provider_route_name}/completions")
49+
async def create_fim(
50+
request: Request,
51+
authorization: str = Header(..., description="Bearer token"),
52+
):
53+
if not authorization.startswith("Bearer "):
54+
raise HTTPException(status_code=401, detail="Invalid authorization header")
55+
56+
api_key = authorization.split(" ")[1]
57+
body = await request.body()
58+
data = json.loads(body)
59+
60+
stream = await self.complete(data, api_key)
61+
return self._completion_handler.create_streaming_response(stream)

src/codegate/server.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from codegate import __description__, __version__
66
from codegate.pipeline.base import PipelineStep, SequentialPipelineProcessor
77
from codegate.pipeline.version.version import CodegateVersion
8+
from codegate.pipeline.fim.secret_analyzer import SecretAnalyzer
89
from codegate.providers.anthropic.provider import AnthropicProvider
910
from codegate.providers.llamacpp.provider import LlamaCppProvider
1011
from codegate.providers.openai.provider import OpenAIProvider
@@ -21,15 +22,20 @@ def init_app() -> FastAPI:
2122
steps: List[PipelineStep] = [
2223
CodegateVersion(),
2324
]
25+
fim_steps: List[PipelineStep] = [
26+
SecretAnalyzer(),
27+
]
2428

2529
pipeline = SequentialPipelineProcessor(steps)
30+
fim_pipeline = SequentialPipelineProcessor(fim_steps)
31+
2632
# Create provider registry
2733
registry = ProviderRegistry(app)
2834

2935
# Register all known providers
30-
registry.add_provider("openai", OpenAIProvider(pipeline_processor=pipeline))
31-
registry.add_provider("anthropic", AnthropicProvider(pipeline_processor=pipeline))
32-
registry.add_provider("llamacpp", LlamaCppProvider(pipeline_processor=pipeline))
36+
registry.add_provider("openai", OpenAIProvider(pipeline_processor=pipeline, fim_pipeline_processor=fim_pipeline))
37+
registry.add_provider("anthropic", AnthropicProvider(pipeline_processor=pipeline, fim_pipeline_processor=fim_pipeline))
38+
registry.add_provider("llamacpp", LlamaCppProvider(pipeline_processor=pipeline, fim_pipeline_processor=fim_pipeline))
3339

3440
# Create and add system routes
3541
system_router = APIRouter(tags=["System"]) # Tags group endpoints in the docs

0 commit comments

Comments
 (0)