Skip to content

Add DeepSeek AI search integration to Codelf #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ app/css
app/js
app/images
app/fonts

# Test files
test-*.js
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,34 @@ CODELF(变量命名神器)

![image](https://user-images.githubusercontent.com/799578/51435509-a2595d00-1cb3-11e9-8f4e-85ecbc3a2325.png)

DeepSeek AI Search Integration
=================

CODELF now supports DeepSeek AI for intelligent code search alongside the traditional SearchCode.com results.

### Configuration

1. Visit [DeepSeek Platform](https://platform.deepseek.com/api_keys) to get your API key
2. In CODELF, click the search source dropdown and select "DeepSeek AI"
3. Enter your API key when prompted
4. Start searching with AI-powered code suggestions

### Features

- **AI-Powered Search**: Get intelligent code suggestions based on your search terms
- **Multiple Language Support**: Search across different programming languages
- **Real-world Examples**: Find practical variable naming examples from AI analysis
- **Seamless Integration**: Switch between SearchCode.com and DeepSeek seamlessly

### Benefits

- More contextual and relevant code suggestions
- Better understanding of your search intent
- Complementary results to traditional code search
- Support for natural language queries

*Note: DeepSeek search requires an API key and is subject to DeepSeek's usage policies and rate limits.*

WIKI
=================
[简体中文](https://github.com/unbug/codelf/wiki)
Expand Down
11 changes: 11 additions & 0 deletions __static/app/src/model/SearchcodeModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ module.exports = new function () {
.addPrimaryKey(['id'], true);

var persistLangsName = 'codelf_langs_selected';
var persistSearchSourceName = 'codelf_search_source';
var langs = Util.localStorage.get(persistLangsName), langQuery;
var searchSource = Util.localStorage.get(persistSearchSourceName) || 'searchcode';
var page = 0;
var lastVal;
var cacheSourceCodes = {};
Expand All @@ -43,6 +45,15 @@ module.exports = new function () {
return langs;
}

this.setSearchSource = function (val) {
searchSource = val || 'searchcode';
Util.localStorage.set(persistSearchSourceName, searchSource);
}

this.getSearchSource = function () {
return searchSource;
}

function genLangQuery(val) {
if (!!val) {
var arr1 = val.replace(/\s+/g, ',').split(','),
Expand Down
88 changes: 85 additions & 3 deletions src/components/SearchBar.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useRef, useState } from 'react';
import { Dropdown, Icon, Input } from 'semantic-ui-react';
import { Dropdown, Icon, Input, Modal, Button, Form } from 'semantic-ui-react';
import * as Configs from '../constants/Configs';

// http://githut.info/
const topProgramLan = [
Expand Down Expand Up @@ -28,12 +29,21 @@ const topProgramLan = [
{ id: 42, language: 'ActionScript' }
];

// Search source options
const searchSources = [
{ key: Configs.SEARCH_SOURCES.SEARCHCODE, text: 'SearchCode', value: Configs.SEARCH_SOURCES.SEARCHCODE },
{ key: Configs.SEARCH_SOURCES.DEEPSEEK, text: 'DeepSeek AI', value: Configs.SEARCH_SOURCES.DEEPSEEK }
];

export default function SearchBar(props) {
const inputEl = useRef(null);
const inputSize = useInputSize('huge');
const [state, setState] = useState({
lang: props.searchLang || [],
valChanged: false
searchSource: props.searchSource || Configs.SEARCH_SOURCES.SEARCHCODE,
valChanged: false,
showDeepSeekModal: false,
deepSeekApiKey: props.deepSeekApiKey || ''
});

function updateState(vals) {
Expand All @@ -43,7 +53,7 @@ export default function SearchBar(props) {
}

function handleSearch() {
props.onSearch(inputEl.current.inputRef.current.value, state.lang);
props.onSearch(inputEl.current.inputRef.current.value, state.lang, state.searchSource);
inputEl.current.inputRef.current.blur();
updateState({ valChanged: false });
}
Expand All @@ -66,6 +76,27 @@ export default function SearchBar(props) {
state.lang.indexOf(id) === -1 ? handleSelectLang(id) : handleDeselectLang(id);
}

function handleSearchSourceChange(e, { value }) {
if (value === Configs.SEARCH_SOURCES.DEEPSEEK && !props.isDeepSeekConfigured) {
updateState({ showDeepSeekModal: true });
return;
}
updateState({ searchSource: value, valChanged: true });
props.onSearchSourceChange && props.onSearchSourceChange(value);
}

function handleDeepSeekApiKeySubmit() {
if (state.deepSeekApiKey.trim()) {
props.onDeepSeekApiKeyChange && props.onDeepSeekApiKeyChange(state.deepSeekApiKey.trim());
updateState({
showDeepSeekModal: false,
searchSource: Configs.SEARCH_SOURCES.DEEPSEEK,
valChanged: true
});
props.onSearchSourceChange && props.onSearchSourceChange(Configs.SEARCH_SOURCES.DEEPSEEK);
}
}

const langItems = topProgramLan.map(key => {
const active = state.lang.indexOf(key.id) !== -1;
return <Dropdown.Item key={key.id}
Expand All @@ -80,6 +111,17 @@ export default function SearchBar(props) {
<div className='search-bar__desc'>
Search over GitHub, Bitbucket, GitLab to find real-world usage variable names
</div>
<div className='search-bar__controls'>
<Dropdown
placeholder='Search Source'
fluid
selection
options={searchSources}
value={state.searchSource}
onChange={handleSearchSourceChange}
style={{ marginBottom: '10px', maxWidth: '200px' }}
/>
</div>
<form action="javascript:void(0);">
<Input ref={inputEl}
onChange={() => updateState({ valChanged: true })}
Expand Down Expand Up @@ -118,6 +160,46 @@ export default function SearchBar(props) {
<a href='https://github.com/unbug/codelf/issues/63'
target='_blank' rel='noopener noreferrer'>Alfred</a>
</div>

{/* DeepSeek API Key Configuration Modal */}
<Modal
open={state.showDeepSeekModal}
onClose={() => updateState({ showDeepSeekModal: false })}
size='small'
>
<Modal.Header>Configure DeepSeek API</Modal.Header>
<Modal.Content>
<Form>
<Form.Field>
<label>DeepSeek API Key</label>
<input
type='password'
placeholder='Enter your DeepSeek API key'
value={state.deepSeekApiKey}
onChange={(e) => updateState({ deepSeekApiKey: e.target.value })}
/>
</Form.Field>
<p>
<small>
You need a DeepSeek API key to use DeepSeek search.
Get your API key from <a href="https://platform.deepseek.com/api_keys" target="_blank" rel="noopener noreferrer">DeepSeek Platform</a>.
</small>
</p>
</Form>
</Modal.Content>
<Modal.Actions>
<Button onClick={() => updateState({ showDeepSeekModal: false })}>
Cancel
</Button>
<Button
primary
onClick={handleDeepSeekApiKeySubmit}
disabled={!state.deepSeekApiKey.trim()}
>
Save & Use DeepSeek
</Button>
</Modal.Actions>
</Modal>
</div>
)
}
Expand Down
21 changes: 20 additions & 1 deletion src/constants/Configs.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,23 @@ const APP_NANE = 'codelf';
const PAGE_URL = Tools.thisPage;
const PAGE_PATH = Tools.thisPath;

export { APP_NANE, PAGE_PATH, PAGE_URL }
// Search source types
const SEARCH_SOURCES = {
SEARCHCODE: 'searchcode',
DEEPSEEK: 'deepseek'
};

// DeepSeek API configuration
const DEEPSEEK_API_BASE = 'https://api.deepseek.com/v1';
const DEEPSEEK_API_KEY_STORAGE = `${APP_NANE}_deepseek_api_key`;
const SEARCH_SOURCE_STORAGE = `${APP_NANE}_search_source`;

export {
APP_NANE,
PAGE_PATH,
PAGE_URL,
SEARCH_SOURCES,
DEEPSEEK_API_BASE,
DEEPSEEK_API_KEY_STORAGE,
SEARCH_SOURCE_STORAGE
}
45 changes: 37 additions & 8 deletions src/containers/MainContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const initState = {
variableRequesting: false,
searchValue: SearchCodeModel.searchValue,
searchLang: SearchCodeModel.searchLang,
searchSource: SearchCodeModel.searchSource,
page: SearchCodeModel.page,
variableList: SearchCodeModel.variableList,
suggestion: SearchCodeModel.suggestion,
Expand All @@ -32,6 +33,8 @@ const initState = {
sourceCodeVisible: false,
sourceCodeVariable: null,
sourceCodeRepo: null,
deepSeekApiKey: SearchCodeModel.getDeepSeekApiKey(),
isDeepSeekConfigured: SearchCodeModel.isDeepSeekConfigured(),
};

function reducer(state, action) {
Expand Down Expand Up @@ -76,7 +79,7 @@ export default function MainContainer(props) {
return () => DDMSModel.offUpdated(handleDDMSModelUpdate);
}, []);

const handleSearch = useCallback((val, lang) => {
const handleSearch = useCallback((val, lang, searchSource) => {
if (val === null || val === undefined || state.variableRequesting) {
return;
}
Expand All @@ -85,13 +88,26 @@ export default function MainContainer(props) {
return;
}
if (val == state.searchValue) {
requestVariable(val, lang);
requestVariable(val, lang, searchSource);
} else {
setState({ searchLang: lang });
setState({ searchLang: lang, searchSource: searchSource });
setTimeout(() => HashHandler.set(val)); // update window.location.hash
}
}, [state.searchValue, state.variableRequesting]);

const handleSearchSourceChange = useCallback((searchSource) => {
setState({ searchSource: searchSource });
SearchCodeModel.setSearchSource(searchSource);
}, []);

const handleDeepSeekApiKeyChange = useCallback((apiKey) => {
SearchCodeModel.setDeepSeekApiKey(apiKey);
setState({
deepSeekApiKey: apiKey,
isDeepSeekConfigured: SearchCodeModel.isDeepSeekConfigured()
});
}, []);

const handleOpenSourceCode = useCallback((variable) => {
setState({ sourceCodeVariable: variable });
setTimeout(() => requestSourceCode(variable.repoList[0]), 0);
Expand Down Expand Up @@ -131,18 +147,19 @@ export default function MainContainer(props) {
return false;
}

function requestVariable(val, lang) {
function requestVariable(val, lang, searchSource) {
const langChanged = lang ? (lang.join(',') != state.searchLang.join(',')) : !!state.searchLang;
const sourceChanged = searchSource && searchSource !== state.searchSource;
val = decodeURIComponent(val);
let page = state.page;
if (val == state.searchValue && !langChanged) {
if (val == state.searchValue && !langChanged && !sourceChanged) {
page += 1;
} else {
page = 0;
}
setState({ searchValue: val, variableRequesting: true });
SearchCodeModel.requestVariable(val, page, lang || state.searchLang);
AppModel.analytics('q=' + val);
SearchCodeModel.requestVariable(val, page, lang || state.searchLang, searchSource || state.searchSource);
AppModel.analytics('q=' + val + (searchSource ? '&source=' + searchSource : ''));
DDMSModel.postKeyWords(val);
updateDocTitle(val);
}
Expand Down Expand Up @@ -175,6 +192,7 @@ export default function MainContainer(props) {
variableRequesting: !mutation.variableList,
searchValue: SearchCodeModel.searchValue,
searchLang: SearchCodeModel.searchLang,
searchSource: SearchCodeModel.searchSource,
page: SearchCodeModel.page,
variableList: SearchCodeModel.variableList,
suggestion: SearchCodeModel.suggestion
Expand All @@ -186,12 +204,23 @@ export default function MainContainer(props) {
sourceCode: SearchCodeModel.sourceCode
});
}
if (mutation.searchSource) {
setState({
searchSource: SearchCodeModel.searchSource
});
}
}

return (
<Container className='main-container'>
<TitleLogo />
<SearchBar placeholder='AI 人工智能' {...state} onSearch={handleSearch} />
<SearchBar
placeholder='AI 人工智能'
{...state}
onSearch={handleSearch}
onSearchSourceChange={handleSearchSourceChange}
onDeepSeekApiKeyChange={handleDeepSeekApiKeyChange}
/>
<Suggestion {...state} />
{state.variableRequesting ? <Loading /> : (state.isError ? <SearchError /> : '')}
{renderSloganImage()}
Expand Down
Loading