#Code Snippet

0 Seguidores · 7 Postagens

Snippet ou fragmento de código é um termo de programação para uma pequena região reutilizável de códigocódigo de máquina ou texto.

Artigo Heloisa Paiva · Jul. 31, 2025 2m read

Se você trabalha com Produções, destacar as conexões entre Business Hosts é um recurso muito conveniente, permitindo aos desenvolvedores obter uma representação visual do fluxo de dados.

Esse recurso funciona por padrão com todos os Business Hosts do sistema. Se um usuário escreve seus próprios Business Services, Processes ou Operations, ele deve implementar o método OnGetConnections para que essa funcionalidade funcione com seus Business Hosts personalizados (ou usar as propriedades  Ens.DataType.ConfigNamepara as conexões).
Dito isso, o SMP mostra apenas a primeira camada de conexões do Business Host selecionado. Às vezes, precisamos obter conexões de conexões recursivamente para construir um grafo completo de fluxo de dados. Ou podemos precisar dessas informações de conexão para verificar quais sistemas downstream podem ser afetados por uma mudança upstream.

0
0 16
Artigo Danusa Calixto · jan 10, 2024 12m read

Temos um delicioso conjunto de dados com receitas escritas por vários usuários do Reddit, porém, a maioria das informações é texto livre, como o título ou a descrição de um post. Vamos descobrir como carregar o conjunto de dados facilmente, extrair algumas características e analisá-las usando recursos do modelo de linguagem grande da OpenAI no Embedded Python e no framework Langchain.

Carregando o conjunto de dados

Primeiro de tudo, precisamos carregar o conjunto de dados ou podemos só nos conectar a ele?

Há diferentes maneiras de alcançar isso: por exemplo, é possível usar o CSV Record Mapper em uma produção de interoperabilidade ou até em aplicativos legais do OpenExchange, como csvgen.

Vamos usar o Foreign Tables. Um recurso bastante útil para projetar dados armazenados fisicamente em outro lugar no IRIS SQL. Podemos usar isso para ter uma primeira visão dos arquivos do conjunto de dados.

Criamos um Foreign Server:

CREATE FOREIGN SERVER dataset FOREIGN DATA WRAPPER CSV HOST '/app/data/'

E, em seguida, uma Foreign Table que se conecta ao arquivo CSV:

CREATE FOREIGN TABLE dataset.Recipes (
  CREATEDDATE DATE,
  NUMCOMMENTS INTEGER,
  TITLE VARCHAR,
  USERNAME VARCHAR,
  COMMENT VARCHAR,
  NUMCHAR INTEGER
) SERVER dataset FILE 'Recipes.csv' USING
{
  "from": {
    "file": {
       "skip": 1
    }
  }
}

É isso, imediatamente podemos executar consultas SQL em "dataset.Recipes": image

## Quais dados são necessários? O conjunto de dados é interessante e estamos com fome. No entanto, se quisermos decidir uma receita para cozinhar, será preciso mais algumas informações que possamos usar para análise.

Vamos trabalhar com duas classes persistentes (tabelas):

  • yummy.data.Recipe: uma classe que contém o título e a descrição da receita e algumas outras propriedades que queremos extrair e analisar, por exemplo, Score, Difficulty, Ingredients, CuisineType, PreparationTime (nota, dificuldade, ingredientes, tipo de culinária, tempo de preparo)
  • yummy.data.RecipeHistory: uma classe simples para registrar o que estamos fazendo com a receita

Podemos agora carregar nossas tabelas "yummy.data*" com o conteúdo do conjunto de dados:

do ##class(yummy.Utils).LoadDataset()

Parece bom, mas ainda precisamos descobrir como vamos gerar os dados para os campos Score, Difficulty, Ingredients, PreparationTime e CuisineType.

## Analise as receitas Queremos processar cada título e descrição de receita e:

  • Extrair informações como Difficulty, Ingredients, CuisineType etc.
  • Criar nossa própria nota com base em nossos critérios para que possamos decidir o que queremos cozinhar.

Vamos usar o seguinte:

LLM (modelos de linguagem grande) são realmente uma ótima ferramenta para processar linguagem natural.

LangChain está pronto para uso no Python, então podemos usá-lo diretamente no InterSystems IRIS usando o Embedded Python.

A classe "SimpleOpenAI" completa fica assim:

/// Análise simples da OpenAI para receitas
Class yummy.analysis.SimpleOpenAI Extends Analysis
{

Property CuisineType As %String;

Property PreparationTime As %Integer;

Property Difficulty As %String;

Property Ingredients As %String;

/// Execute
/// Você pode tentar isto a partir de um terminal:
/// set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8))
/// do a.Run()
/// zwrite a
Method Run()
{
    try {
        do ..RunPythonAnalysis()

        set reasons = ""

        // meus tipos de culinária favoritos
        if "spanish,french,portuguese,italian,korean,japanese"[..CuisineType {
            set ..Score = ..Score + 2
            set reasons = reasons_$lb("It seems to be a "_..CuisineType_" recipe!")
        }

        // não quero passar o dia todo cozinhando :)
        if (+..PreparationTime < 120) {
            set ..Score = ..Score + 1
            set reasons = reasons_$lb("You don't need too much time to prepare it") 
        }
        
        // bônus para ingredientes favoritos!
        set favIngredients = $listbuild("kimchi", "truffle", "squid")
        for i=1:1:$listlength(favIngredients) {
            set favIngred = $listget(favIngredients, i)
            if ..Ingredients[favIngred {
                set ..Score = ..Score + 1
                set reasons = reasons_$lb("Favourite ingredient found: "_favIngred)
            }
        }

        set ..Reason = $listtostring(reasons, ". ")

    } catch ex {
        throw ex
    }
}

/// Atualize a receita com o resultado da análise
Method UpdateRecipe()
{
    try {
        // chame a implementação de classe mãe primeiro
        do ##super()

        // adicione resultados de análises da OpenAI específicos
        set ..Recipe.Ingredients = ..Ingredients
        set ..Recipe.PreparationTime = ..PreparationTime
        set ..Recipe.Difficulty = ..Difficulty
        set ..Recipe.CuisineType = ..CuisineType

    } catch ex {
        throw ex
    }
}

/// Execute a análise usando o embedded Python + Langchain
/// do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8)).RunPythonAnalysis(1)
Method RunPythonAnalysis(debug As %Boolean = 0) [ Language = python ]
{
    # load OpenAI APIKEY from env
    import os
    from dotenv import load_dotenv, find_dotenv
    _ = load_dotenv('/app/.env')

    # account for deprecation of LLM model
    import datetime
    current_date = datetime.datetime.now().date()
    # date after which the model should be set to "gpt-3.5-turbo"
    target_date = datetime.date(2024, 6, 12)
    # set the model depending on the current date
    if current_date > target_date:
        llm_model = "gpt-3.5-turbo"
    else:
        llm_model = "gpt-3.5-turbo-0301"

    from langchain.chat_models import ChatOpenAI
    from langchain.prompts import ChatPromptTemplate
    from langchain.chains import LLMChain

    from langchain.output_parsers import ResponseSchema
    from langchain.output_parsers import ResponseSchema

    # init llm model
    llm = ChatOpenAI(temperature=0.0, model=llm_model)

    # prepare the responses we need
    cuisine_type_schema = ResponseSchema(
        name="cuisine_type",
        description="What is the cuisine type for the recipe? \
                     Answer in 1 word max in lowercase"
    )
    preparation_time_schema = ResponseSchema(
        name="preparation_time",
        description="How much time in minutes do I need to prepare the recipe?\
                     Anwer with an integer number, or null if unknown",
        type="integer",
    )
    difficulty_schema = ResponseSchema(
        name="difficulty",
        description="How difficult is this recipe?\
                     Answer with one of these values: easy, normal, hard, very-hard"
    )
    ingredients_schema = ResponseSchema(
        name="ingredients",
        description="Give me a comma separated list of ingredients in lowercase or empty if unknown"
    )
    response_schemas = [cuisine_type_schema, preparation_time_schema, difficulty_schema, ingredients_schema]

    # get format instructions from responses
    output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
    format_instructions = output_parser.get_format_instructions()
    
    analysis_template = """\
    Interprete and evaluate a recipe which title is: {title}
    and the description is: {description}
    
    {format_instructions}
    """
    prompt = ChatPromptTemplate.from_template(template=analysis_template)

    messages = prompt.format_messages(title=self.Recipe.Title, description=self.Recipe.Description, format_instructions=format_instructions)
    response = llm(messages)

    if debug:
        print("======ACTUAL PROMPT")
        print(messages[0].content)
        print("======RESPONSE")
        print(response.content)

    # populate analysis with results
    output_dict = output_parser.parse(response.content)
    self.CuisineType = output_dict['cuisine_type']
    self.Difficulty = output_dict['difficulty']
    self.Ingredients = output_dict['ingredients']
    if type(output_dict['preparation_time']) == int:
        self.PreparationTime = output_dict['preparation_time']

    return 1
}

}

O método "RunPythonAnalysis" é onde tudo relacionado a OpenAI acontece :). Você pode executá-lo diretamente no terminal para uma receita específica:

do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)

Vamos obter uma saída assim:

USER>do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)
======ACTUAL PROMPT
                    Interprete and evaluate a recipe which title is: Folded Sushi - Alaska Roll
                    and the description is: Craving for some sushi but don't have a sushi roller? Try this easy version instead. It's super easy yet equally delicious!
[Video Recipe](https://www.youtube.com/watch?v=1LJPS1lOHSM)
# Ingredients
Serving Size:  \~5 sandwiches      
* 1 cup of sushi rice
* 3/4 cups + 2 1/2 tbsp of water
* A small piece of konbu (kelp)
* 2 tbsp of rice vinegar
* 1 tbsp of sugar
* 1 tsp of salt
* 2 avocado
* 6 imitation crab sticks
* 2 tbsp of Japanese mayo
* 1/2 lb of salmon  
# Recipe     
* Place 1 cup of sushi rice into a mixing bowl and wash the rice at least 2 times or until the water becomes clear. Then transfer the rice into the rice cooker and add a small piece of kelp along with 3/4 cups plus 2 1/2 tbsp of water. Cook according to your rice cookers instruction.
* Combine 2 tbsp rice vinegar, 1 tbsp sugar, and 1 tsp salt in a medium bowl. Mix until everything is well combined.
* After the rice is cooked, remove the kelp and immediately scoop all the rice into the medium bowl with the vinegar and mix it well using the rice spatula. Make sure to use the cut motion to mix the rice to avoid mashing them. After thats done, cover it with a kitchen towel and let it cool down to room temperature.
* Cut the top of 1 avocado, then slice into the center of the avocado and rotate it along your knife. Then take each half of the avocado and twist. Afterward, take the side with the pit and carefully chop into the pit and twist to remove it. Then, using your hand, remove the peel. Repeat these steps with the other avocado. Dont forget to clean up your work station to give yourself more space. Then, place each half of the avocado facing down and thinly slice them. Once theyre sliced, slowly spread them out. Once thats done, set it aside.
* Remove the wrapper from each crab stick. Then, using your hand, peel the crab sticks vertically to get strings of crab sticks. Once all the crab sticks are peeled, rotate them sideways and chop them into small pieces, then place them in a bowl along with 2 tbsp of Japanese mayo and mix until everything is well mixed.
* Place a sharp knife at an angle and thinly slice against the grain. The thickness of the cut depends on your preference. Just make sure that all the pieces are similar in thickness.
* Grab a piece of seaweed wrap. Using a kitchen scissor, start cutting at the halfway point of seaweed wrap and cut until youre a little bit past the center of the piece. Rotate the piece vertically and start building. Dip your hand in some water to help with the sushi rice. Take a handful of sushi rice and spread it around the upper left hand quadrant of the seaweed wrap. Then carefully place a couple slices of salmon on the top right quadrant. Then place a couple slices of avocado on the bottom right quadrant. And finish it off with a couple of tsp of crab salad on the bottom left quadrant. Then, fold the top right quadrant into the bottom right quadrant, then continue by folding it into the bottom left quadrant. Well finish off the folding by folding the top left quadrant onto the rest of the sandwich. Afterward, place a piece of plastic wrap on top, cut it half, add a couple pieces of ginger and wasabi, and there you have it.
                    
                    A saída deve ser um fragmento de código markdown formatado no seguinte esquema, incluindo o "```json" e "```" à esquerda e à direita:
json
{
        "cuisine_type": string  // What is the cuisine type for the recipe?                                  Answer in 1 word max in lowercase
        "preparation_time": integer  // How much time in minutes do I need to prepare the recipe?                                    Anwer with an integer number, or null if unknown
        "difficulty": string  // How difficult is this recipe?                               Answer with one of these values: easy, normal, hard, very-hard
        "ingredients": string  // Give me a comma separated list of ingredients in lowercase or empty if unknown
}

                    
======RESPONSE
json
{
        "cuisine_type": "japanese",
        "preparation_time": 30,
        "difficulty": "easy",
        "ingredients": "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
}

Tudo bem. Parece que nosso prompt da OpenAI é capaz de retornar algumas informações úteis. Vamos executar toda a classe da análise no terminal:

set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12))
do a.Run()
zwrite a
USER>zwrite a
a=37@yummy.analysis.SimpleOpenAI  ; <OREF>
+----------------- general information ---------------
|      oref value: 37
|      class name: yummy.analysis.SimpleOpenAI
| reference count: 2
+----------------- attribute values ------------------
|        CuisineType = "japanese"
|         Difficulty = "easy"
|        Ingredients = "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
|    PreparationTime = 30
|             Reason = "It seems to be a japanese recipe!. You don't need too much time to prepare it"
|              Score = 3
+----------------- swizzled references ---------------
|           i%Recipe = ""
|           r%Recipe = "30@yummy.data.Recipe"
+-----------------------------------------------------

## Analisando todas as receitas! Naturalmente, você gostaria de executar a análise em todas as receitas que carregamos.

Você pode analisar uma variedade de IDs de receitas desta forma:

USER>do ##class(yummy.Utils).AnalyzeRange(1,10)
> Recipe 1 (1.755185s)
> Recipe 2 (2.559526s)
> Recipe 3 (1.556895s)
> Recipe 4 (1.720246s)
> Recipe 5 (1.689123s)
> Recipe 6 (2.404745s)
> Recipe 7 (1.538208s)
> Recipe 8 (1.33001s)
> Recipe 9 (1.49972s)
> Recipe 10 (1.425612s)

Depois disso, verifique novamente sua tabela de receitas e confira os resultados

select * from yummy_data.Recipe

image

Acho que eu poderia tentar a pizza de abóbora ou o tofu com kimchi coreano e porco :). Vou precisar conferir novamente em casa de qualquer forma :)

Observações finais

Encontre o exemplo completo em https://github.com/isc-afuentes/recipe-inspector

Com esse exemplo simples, aprendemos a usar as técnicas de LLM para adicionar recursos ou analisar partes dos seus dados no InterSystems IRIS.

Com esse ponto de partida, você pode pensar em:

  • Usar a BI do InterSystems para explorar e navegar pelos seus dados usando cubos e painéis.
  • Criar um webapp e fornecer IU (por exemplo, Angular). Para isso, você pode aproveitar pacotes como RESTForms2 para gerar automaticamente as APIs REST para suas classes persistentes.
  • Armazenar as receitas independentemente de gostar delas e, depois, tentar determinar se uma nova receita gostará de você? Você pode tentar uma abordagem com o IntegratedML ou até mesmo um LLM, fornecendo alguns dados de exemplo e criando um caso de uso de RAG (Geração Aumentada de Recuperação).

O que mais você pode tentar? Me diga o que você acha!

0
0 110
Artigo Cristiano Silva · Ago. 7, 2023 2m read

Transformações DTL e chamadas GetValueAt/SetValueAt em mensagens HL7 truncarão todos os campos com mais de 32 K. Para evitar isso, os métodos GetFieldStreamRaw e StoreFieldStreamRaw devem ser utilizados ao lidar com campos que possam ser maiores que 32K. OBX:5 é um exemplo frequente. Esses métodos têm algumas sutilezas e devem ser usados com cuidado.

0
0 61
Artigo Eduard Lebedyuk · Nov. 19, 2021 2m read

Recentemente eu precisei gerar uma especificação Swagger a partir de classes persistentes e seriais, então estou publicando meu código (ainda incompleto - você ainda precisa  resolver detalhes específicos do aplicativo mas é um começo). Ele está disponível aqui.

Digamos que você tem estas classes:

2
0 214
Artigo Andre Larsen Barbosa · Ago. 18, 2021 2m read

ObjectScript tem pelo menos três maneiras de lidar com erros (códigos de status, exceções, SQLCODE, etc.).A maior parte do código do sistema usa status, mas as exceções são mais fáceis de tratar por vários motivos.Trabalhando com código legado, você passa algum tempo traduzindo entre as diferentes técnicas.Eu uso muito esses trechos para referência.Esperançosamente, eles também são úteis para outras pessoas. 

0
0 173
Artigo Gabriel Schiehl · Jul. 7, 2021 1m read

Recentemente, eu queria obter uma lista de todas as consultas em cache e seus textos. Veja como fazer isso.
Primeiro, crie um procedimento SQL retornando texto de consulta de cache a partir de um nome de rotina de consulta em cache:

Class test.CQ
{

/// SELECT test.CQ_GetText()
ClassMethod GetText(routine As %String) As %String [ CodeMode = expression, SqlProc ]
{
##class(%SQLCatalog).GetCachedQueryInfo(routine)
}

}
E depois disso, você pode executar esta consulta:
SELECT Routine, test.CQ_GetText(Routine)
FROM %SQL_Manager.CachedQueryTree()
E obtenha uma lista de consultas em cache:
0
0 222
Artigo Kyle Baxter · jan 18, 2021 1m read

As consultas utilizando intervalo de datas estão muito lentas para você?  O desempenho do SQL te desanima?  Eu tenho um estranho truque que pode te ajudar! (Desenvolvedores de SQL odeiam isso!)*

Se você tiver uma classe que registra os timestamps quando os dados são adicionados, esses dados estarão em sequência com seus valores IDKEY - isto é, TimeStamp< TimeStampse e somente se ID1 < IDpara todos os IDs e valores de timestamps na tabela - então, você pode usar esse conhecimento para aumentar o desempenho de consultas para intervalos de timestamps.  Considere a seguinte tabela:

Class User.TSOrder extends %Persistent 
{ 

Property TS as %TimeStamp;

Property Data as %String (MAXLEN=100, MINLEN=200);

Index TSIdx on TS;

Index Extent [type=bitmap, extent];

}

Popular isso com 30.000.000 de linhas aleatórias com datas dos últimos 30 dias, resultará em 1.000.000 de linhas por dia.  Agora, se quisermos consultar as informações de um determinado dia, você pode escrever o seguinte

SELECT ID, TS, Data 
FROM TSOrder
WHERE 
     TS >= '2016-07-01 00:00:00.00000' AND 
     TS <= '2016-07-01 23:59:59.999999'

Uma consulta razoável, com certeza.  No meu sistema, no entanto, isso realizou 2.172.792 referências a globais em 7,2 segundos.  Mas, sabendo que os IDs e TimeStamps estão na mesma ordem, podemos usar os TimeStamps para obter um intervalo de ID.  Considere a seguinte consulta:

SELECT ID, TS, Data
FROM TSOrder
WHERE 
     ID >= (SELECT TOP 1 ID FROM TSOrder WHERE TS >='2016-07-01 00:00:00.00000' ORDER BY TS ASC) AND 
     ID <= (SELECT TOP 1 ID FROM TSOrder WHERE TS <='2016-07-01 23:59:59.999999' ORDER BY TS DESC)

A nova consulta é concluída em 5,1 segundos e realiza apenas 999.985 referências a globais**!  

Essa técnica pode ser aplicada de forma mais pragmática a tabelas com mais campos indexados e consultas que possuem várias cláusulas WHERE.  O intervalo de ID gerado a partir das subconsultas pode ser colocado no formato de bitmap, gerando uma velocidade incrível quando você tem uma solução de vários índices.   A tabela Ens.MessageHeader é um ótimo exemplo onde você pode colocar para funcionar esse truque.

Vamos ser claros - este é um EXEMPLO de uma vitória.  Se você tiver muitas condições na cláusula WHERE na mesma tabela (e elas estão indexadas), Então esta técnica pode lhe dar GRANDES vitórias!  Experimente em suas consultas!  

  • Desenvolvedores de SQL não odeiam isso, mas se a Internet nos ensinou alguma coisa é que bordões obtêm mais tráfego.

** Ao testar as consultas que retornam muitas linhas, o SMP não consegue lidar com isso, e a maior parte do tempo é gasto na exibição dos dados.  A maneira adequada de testar é com o SQL integrado ou dinâmico, examinando os resultados, mas gerando a saída deles por um tempo, e usando o SQL Shell para suas contagens de globais.  Você também pode usar o SQL Stats para isso.

0
0 227