Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<br />
<a href="https://discord.gg/sGdnKwB3qq" target="_blank">
<img alt="Discord Community" src="https://img.shields.io/badge/Discord-Community-5865F2?logo=discord&labelColor=white">
</a> <a href="https://goreportcard.com/report/github.com/HexmosTech/git-lrc" target="_blank" rel="noopener noreferrer"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/HexmosTech/git-lrc"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/confidence.yml" target="_blank" rel="noopener noreferrer"><img alt="confidence.yml" title="confidence.yml: Minimum confidence workflow" src="https://github.com/HexmosTech/git-lrc/actions/workflows/confidence.yml/badge.svg"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/status-doc-link-check.yml" target="_blank" rel="noopener noreferrer"><img alt="status-doc-link-check.yml" title="status-doc-link-check.yml: Status document integrity workflow" src="https://github.com/HexmosTech/git-lrc/actions/workflows/status-doc-link-check.yml/badge.svg"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/gitleaks.yml" target="_blank" rel="noopener noreferrer"><img alt="gitleaks.yml" title="gitleaks.yml: Secret scanning workflow" src="https://github.com/HexmosTech/git-lrc/actions/workflows/gitleaks.yml/badge.svg"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/osv-scanner.yml" target="_blank" rel="noopener noreferrer"><img alt="osv-scanner.yml" title="osv-scanner.yml: Dependency vulnerability scan" src="https://github.com/HexmosTech/git-lrc/actions/workflows/osv-scanner.yml/badge.svg"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/govulncheck.yml" target="_blank" rel="noopener noreferrer"><img alt="govulncheck.yml" title="govulncheck.yml: Go vulnerability check" src="https://github.com/HexmosTech/git-lrc/actions/workflows/govulncheck.yml/badge.svg"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/semgrep.yml" target="_blank" rel="noopener noreferrer"><img alt="semgrep.yml" title="semgrep.yml: Static analysis security scan" src="https://github.com/HexmosTech/git-lrc/actions/workflows/semgrep.yml/badge.svg"></a>&nbsp;<img alt="dependabot-enabled" title="dependabot-enabled: Automated dependency updates are enabled" src="./gfx/dependabot-enabled.svg">
</a> <a href="https://goreportcard.com/report/github.com/HexmosTech/git-lrc" target="_blank" rel="noopener noreferrer"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/HexmosTech/git-lrc"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/gitleaks.yml" target="_blank" rel="noopener noreferrer"><img alt="gitleaks.yml" title="gitleaks.yml: Secret scanning workflow" src="https://github.com/HexmosTech/git-lrc/actions/workflows/gitleaks.yml/badge.svg"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/osv-scanner.yml" target="_blank" rel="noopener noreferrer"><img alt="osv-scanner.yml" title="osv-scanner.yml: Dependency vulnerability scan" src="https://github.com/HexmosTech/git-lrc/actions/workflows/osv-scanner.yml/badge.svg"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/govulncheck.yml" target="_blank" rel="noopener noreferrer"><img alt="govulncheck.yml" title="govulncheck.yml: Go vulnerability check" src="https://github.com/HexmosTech/git-lrc/actions/workflows/govulncheck.yml/badge.svg"></a>&nbsp;<a href="https://github.com/HexmosTech/git-lrc/actions/workflows/semgrep.yml" target="_blank" rel="noopener noreferrer"><img alt="semgrep.yml" title="semgrep.yml: Static analysis security scan" src="https://github.com/HexmosTech/git-lrc/actions/workflows/semgrep.yml/badge.svg"></a>&nbsp;<img alt="dependabot-enabled" title="dependabot-enabled: Automated dependency updates are enabled" src="./gfx/dependabot-enabled.svg">
</div>

<br />
Expand Down Expand Up @@ -241,6 +241,17 @@ By default, the **first connector in the list** is used for reviews.

![LRC UI connector management preview](./gfx/git-lrc-ui.png)

### Compatible API Support

We also support:
- Anthropic Compatible API

To configure:
- Select the option `Anthropic Compatible API` connector from the UI.
- Set the base URL for the API connector by selecting from the dropdown (e.g., [`gw.claudeapi.com`](https://claudeapi.com?utm_source=git-lrc)) or entering a custom URL.
- Enter your API key.
- Select the Claude model to use for reviews.

## Security You Can Trust

- Security is treated as a core product requirement in git-lrc.
Expand Down Expand Up @@ -362,7 +373,6 @@ Pick the right place based on what you need:
- **Discord**: [discord.gg/sGdnKwB3qq](https://discord.gg/sGdnKwB3qq) - Best for joining the community, asking general Q&A, and having quick back-and-forth with the team.
- **GitHub Discussions**: [github.com/HexmosTech/git-lrc/discussions](https://github.com/HexmosTech/git-lrc/discussions) - For in-depth idea proposals, scoping, design discussions, modification proposals, and constructive criticism.
- **GitHub Issues**: [github.com/HexmosTech/git-lrc/issues](https://github.com/HexmosTech/git-lrc/issues) - For concrete, scoped tasks such as bugs, focused feature requests, and actionable implementation work.
- **Contributing Guide**: See [CONTRIBUTING.md](./CONTRIBUTING.md) for contributor workflow, local setup, testing expectations, and pull request guidance.

## License

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/HexmosTech/git-lrc

go 1.25.10
go 1.25.11

require (
charm.land/bubbletea/v2 v2.0.2
Expand Down
6 changes: 4 additions & 2 deletions internal/staticserve/static/ui-connectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,10 @@ function App() {
connector_name: isLikelyAutoGeneratedName(prev.connector_name, providers)
? generateFriendlyConnectorName(provider.id, providers)
: prev.connector_name,
selected_model: '',
base_url: provider.requiresBaseURL ? prev.base_url : '',
selected_model: provider.modelPresets?.length ? provider.modelPresets[0] : '',
base_url: provider.requiresBaseURL
? (provider.baseURLPresets?.length ? provider.baseURLPresets[0].value : '')
: '',
}));
if (provider.id !== 'ollama') {
setOllamaModels([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,47 @@ export function ConnectorFormPage({
/>

${showBaseURL
? html`
<label>Base URL (required)</label>
<input
type="url"
required
placeholder=${selectedProvider.baseURLPlaceholder || 'http://localhost:11434/ollama/api'}
value=${form.base_url}
onInput=${(event) => onFieldChange('base_url', event.target.value)}
/>
`
? (() => {
const presets = selectedProvider.baseURLPresets || [];
const CUSTOM = '__custom__';
const effectivePreset = presets.find((p) => p.value === form.base_url) ? form.base_url : CUSTOM;
if (presets.length > 0) {
return html`
<label>Base URL (required)</label>
<select
value=${effectivePreset}
onChange=${(e) => {
const v = e.target.value;
onFieldChange('base_url', v === CUSTOM ? '' : v);
}}
>
${presets.map((p) => html`<option value=${p.value}>${p.label}</option>`)}
<option value=${CUSTOM}>Custom URL...</option>
</select>
${effectivePreset === CUSTOM
? html`
<input
type="url"
required
placeholder=${selectedProvider.baseURLPlaceholder || 'https://'}
value=${form.base_url}
onInput=${(event) => onFieldChange('base_url', event.target.value)}
/>
`
: ''}
`;
}
return html`
<label>Base URL (required)</label>
<input
type="url"
required
placeholder=${selectedProvider.baseURLPlaceholder || 'http://localhost:11434/ollama/api'}
value=${form.base_url}
onInput=${(event) => onFieldChange('base_url', event.target.value)}
/>
`;
})()
: ''}

${isOllama
Expand Down Expand Up @@ -223,13 +254,42 @@ export function ConnectorFormPage({
: ''}
</div>
`
: html`
<input
value=${form.selected_model}
placeholder="Enter model ID (e.g., gpt-4o)"
onInput=${(event) => onFieldChange('selected_model', event.target.value)}
/>
`}
: (() => {
const selectedBasePreset = (selectedProvider.baseURLPresets || []).find((p) => p.value === form.base_url);
const mPresets = selectedBasePreset?.models || selectedProvider.modelPresets || [];
const CUSTOM_MODEL = '__custom_model__';
if (mPresets.length > 0) {
const effectiveModel = mPresets.includes(form.selected_model) ? form.selected_model : CUSTOM_MODEL;
return html`
<select
value=${effectiveModel}
onChange=${(e) => {
const v = e.target.value;
onFieldChange('selected_model', v === CUSTOM_MODEL ? '' : v);
}}
>
${mPresets.map((m) => html`<option value=${m}>${m}</option>`)}
<option value=${CUSTOM_MODEL}>+ Custom</option>
</select>
${effectiveModel === CUSTOM_MODEL
? html`
<input
value=${form.selected_model}
placeholder="Enter model ID (e.g., claude-haiku-4-5-20251001)"
onInput=${(event) => onFieldChange('selected_model', event.target.value)}
/>
`
: ''}
`;
}
return html`
<input
value=${form.selected_model}
placeholder="Enter model ID (e.g., gpt-4o)"
onInput=${(event) => onFieldChange('selected_model', event.target.value)}
/>
`;
})()}
`
}

Expand Down
23 changes: 23 additions & 0 deletions internal/staticserve/static/ui-connectors/providers.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ export const providers = [
name: 'Anthropic Claude',
apiKeyPlaceholder: 'sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
},
{
id: 'anthropic-compatible',
name: 'Anthropic Compatible API',
requiresBaseURL: true,
baseURLPresets: [
{
label: 'ClaudeAPI (gw.claudeapi.com)',
value: 'https://gw.claudeapi.com/v1',
models: ['claude-haiku-4-5-20251001', 'claude-sonnet-4-6', 'claude-opus-4-8'],
},
],
baseURLPlaceholder: 'https://your-anthropic-compatible-endpoint.com',
apiKeyPlaceholder: 'sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
modelPresets: [
'claude-haiku-4-5-20251001',
'claude-sonnet-4-6',
'claude-opus-4-8',
'claude-3-5-sonnet-20241022',
'claude-3-5-haiku-20241022',
'claude-3-opus-20240229',
'claude-3-haiku-20240307',
],
},
];

export function defaultForm() {
Expand Down
1 change: 1 addition & 0 deletions setup/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,5 @@ type CreateConnectorRequest struct {
ConnectorName string `json:"connector_name"`
SelectedModel string `json:"selected_model"`
DisplayOrder int `json:"display_order"`
BaseURL string `json:"base_url,omitempty"`
}
Loading